Reputation: 1337
Consider the following code:
public KeyAttribute : Attribute{
public string Value;
public KeyAttribute(string value){
Value = value;
}
}
[Key("A")]
[Key("AB")]
public class A : IClass
{
public string FieldA {get;set;}
}
[Key("B")]
public class B : IClass
{
public string FieldB {get;set;}
}
So I have to through all the implementations of IClass
interface, and I've got to construct a dictionary Dictionary<string, ConstructorInfo>
where the keys are the Value
properties of KeyAttribute
, and the values are the corresponding ConstructorInfo
.
Notice that there might be several KeyAttribute
on a single class, so for that case, there should be the corresponding amount of entries in the dictionary.
For the current example, the desired outcome is:
key | value
--------+-------------------
"A" | ConstructorInfo A
"AB" | ConstructorInfo A
"B" | ConstructorInfo B
At first I wrote this:
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.ToDictionary(t =>
{
var key = (KeyAttribute) t.GetCustomAttributes(typeof(KeyAttribute), true).First();
return key.Value;
}, t => t.GetConstructors(BindingFlags.Public).First());
But as you can see, the code above does not handle the situation with several attributes. So I did the following, but it's obviously wrong and I'm not sure how to correct it.
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.ToDictionary(t =>
{
return t.GetCustomAttributes(typeof(KeyAttribute), true).Select(a => ((KeyAttribute)a).Value);
}, t => t.GetConstructors(BindingFlags.Public).First());
I know I can do that without LINQ like that:
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t));
var dict = new Dictionary<string, ConstructorInfo>();
foreach (var type in types)
{
var keys = type.GetCustomAttributes(typeof(KeyAttribute), true).Cast<KeyAttribute>().Select(a => a.Value);
var ctorInfo = type.GetConstructors(BindingFlags.Public).First();
foreach (var key in keys)
{
dict.Add(key, ctorInfo);
}
}
return dict;
But I'd rather stick to LINQ, if it is possible.
Sorry about all these somewhat misleading details about attributes and all that, while it is a question about LINQ, but I couldn't think of any other vivid example.
Upvotes: 1
Views: 3813
Reputation: 32445
Use SelectMany
for key attributes
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.SelectMany(t =>
{
return t.GetCustomAttributes<KeyAttribute>()
.Select(ka => new
{
Key = ka,
Ctor = t.GetConstructors(BindingFlags.Public).First()
});
})
.ToDictionary(t => t.Key, t.Ctor);
Upvotes: 1
Reputation: 38179
This should work:
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.SelectMany(t => t.GetCustomAttributes(typeof(KeyAttribute), true)
.Select(a => new
{
constInfo = t.GetConstructors(BindingFlags.Public).First(),
attrVal = ((KeyAttribute)a).Value
}))
.ToDictionary(entry => entry.attrVal, entry => entry.constInfo);
Upvotes: 4