Reputation: 3070
I have this as my class signature:
public class LocationRule : IRule<Location>
I am trying to use reflection
to access the type of the implementation of IRule
.
Using this code
Assembly.GetTypes()
.Where(type => typeof(IRule<Location>).IsAssignableFrom(type)
&& !type.IsInterface)
I can get the classes that inherit this specific implementation of IRule
. However there is no way to dynamically insert a type into this expression
like so.
Assembly.GetTypes()
.Where(type => typeof(IRule<typeof(AnotherClass)>).IsAssignableFrom(type)
&& !type.IsInterface)
Is there away to find all classes that implement IRule<>
and then find out all the types that are implemented?
public class LocationRule : IRule<Location>
public class NameRule : IRule<Name>
Is there a way to get hold of each of these types? Name and Location, so I can place them in a Dictionary<TypeOfIRuleGeneric, TypeOfRuleClass>
i.e Key = Location, Value = LocationRule?
Thanks.
Upvotes: 1
Views: 898
Reputation: 7674
You're looking for some kind of "commonality" between all IRule<T>
types. You could define a base interface as dcg suggested, but that won't lead into the second part of your question where you want to pull out the generic type parameters and insert them into a dictionary.
There is a thing called a generic type definition that represents a "stripped-down" generic type, with all of the generic type parameters removed. You can use this as the "commonality" instead.
typeof(IRule<Location>).GetGenericTypeDefinition() // MyApp.IRule`1[T]
But C# lets you actually use an unspecified generic type within typeof
to give you the same thing more succinctly:
typeof(IRule<>) // compiles! Also gives MyApp.IRule`1[T]
IsAssignableFrom
won't be useful here because you cannot instantiate an unconstructed generic type to begin with.
I made a helper method that gets all generic interfaces implemented by a type:
public static IEnumerable<Type> GetGenericInterfaces(this Type type)
{
return type.GetInterfaces().Where(t => t.IsGenericType);
}
Here's another method that tells me whether I can construct a type from a given generic type definition:
public static bool IsConstructableFrom(this Type type, Type genericTypeDefinition)
{
return type.IsConstructedGenericType &&
(type.GetGenericTypeDefinition() == genericTypeDefinition);
}
Now your query would be:
var types = assembly.GetTypes().Where(type =>
!type.IsInterface &&
type.GetGenericInterfaces().Any(generic => generic.IsConstructableFrom(typeof(IRule<>))))
.ToArray();
But ultimately you want a Dictionary<TypeOfIRuleGeneric, TypeOfRuleClass>
, where the key is the generic type parameter of IRule<>
and the value is the class that implements it. I am going to assume that you'll have at most one class that implements IRule<T>
for a particular T
(is this assumption true?). I'll also assume that each class will implement at most one IRule<T>
(is this assumption true?).
There's a lot of ways to do that part. I came up with this:
var dict = assembly.GetTypes()
.Where(type => !type.IsInterface)
.Select(type => new
{
TypeOfRuleClass = type,
IRuleInterface = type
.GetGenericInterfaces().FirstOrDefault(generic => generic.IsConstructableFrom(typeof(IRule<>)))
})
.Where(t => t.IRuleInterface != null)
.ToDictionary(t => t.TypeOfRuleClass, t => t.IRuleInterface.GetGenericArguments()[0]);
Where
filters out interfaces from the candidate typesSelect
transforms each candidate type into a tuple (TypeOfRuleClass, IRuleInterface)
where IRuleInterface
is the first (and only, by assumption) matching IRule<T>
type implemented by TypeOfRuleClass
; or, it's null
if the type does not implement IRule<T>
Where
filters out candidate types that do not implement IRule<T>
ToDictionary
creates the dictionary you wanted, where the key is TypeOfRuleClass
and the value is the generic type parameter.Upvotes: 4
Reputation: 4219
If you want to get all types that implements a specific interface you can do it like the following:
public interface IMyInterface { }
public class MyClass: IMyInterface { }
//...
var types = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => !t.IsInterface && t.GetInterfaces().Contains(typeof(IMyInterface)))
.ToList();
Here types
would be populated with MyClass
.
Upvotes: 1