简体   繁体   中英

Is there a way to get the type of an inherited generic class using reflection?

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> ie Key = Location, Value = LocationRule?

Thanks.

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]);
  • The first Where filters out interfaces from the candidate types
  • The Select 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>
  • The second Where filters out candidate types that do not implement IRule<T>
  • The ToDictionary creates the dictionary you wanted, where the key is TypeOfRuleClass and the value is the generic type parameter.

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM