Get all types implementing specific open generic type
Asked Answered
B

4

76

How do I get all types that implementing a specific open generic type?

For instance:

public interface IUserRepository : IRepository<User>

Find all types that implement IRepository<>.

public static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
  ...
}
Baudelaire answered 27/12, 2011 at 13:57 Comment(0)
P
103

This will return all types that inherit a generic base class. Not all types that inherit a generic interface.

var AllTypesOfIRepository = from x in Assembly.GetAssembly(typeof(AnyTypeInTargetAssembly)).GetTypes()
 let y = x.BaseType
 where !x.IsAbstract && !x.IsInterface &&
 y != null && y.IsGenericType &&
 y.GetGenericTypeDefinition() == typeof(IRepository<>)
 select x;

This will return all types, including interfaces, abstracts, and concrete types that have the open generic type in its inheritance chain.

public static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
    return from x in assembly.GetTypes()
            from z in x.GetInterfaces()
            let y = x.BaseType
            where
            (y != null && y.IsGenericType &&
            openGenericType.IsAssignableFrom(y.GetGenericTypeDefinition())) ||
            (z.IsGenericType &&
            openGenericType.IsAssignableFrom(z.GetGenericTypeDefinition()))
            select x;
}

This second method will find ConcreteUserRepo and IUserRepository in this example:

public class ConcreteUserRepo : IUserRepository
{}

public interface IUserRepository : IRepository<User>
{}

public interface IRepository<User>
{}

public class User
{}
Plataea answered 27/12, 2011 at 14:7 Comment(5)
Yes, but you will only get the concreterepo and irepo when it is in the same assembly. But this is absolutely ok.Baudelaire
Good catch! We'd have to be careful to load all the dependent assemblies, not just scan the currently loaded. Here's a SO about loading all assemblies: #2385092Plataea
The typeof(IRepository<>) works only if the generic type has a single generic parameter. Any ideas for types that have more than one generic type parameter?Streit
Sorry, I found the answer. All you have to do is typeof(IRepository<,>) (assuming two generic type parameters) and so on! Cool!Streit
Won't this only work when the concrete type directly inherits the interface being searched for?Galliard
C
10

Solution implemented without LINQ, searching both generic and non generic interfaces, filtering the return type to classes.

public static class SampleCode
{
    public static void Main()
    {
        IList<Type> loadableTypes;

        // instance the dummy class used to find the current assembly
        DummyClass dc = new DummyClass();

        loadableTypes = GetClassesImplementingAnInterface(dc.GetType().Assembly, typeof(IMsgXX)).Item2;
        foreach (var item in loadableTypes) {Console.WriteLine("1: " + item);}
        // print
        // 1: Start2.MessageHandlerXY

        loadableTypes = GetClassesImplementingAnInterface(dc.GetType().Assembly, typeof(IHandleMessageG<>)).Item2;
        foreach (var item in loadableTypes) { Console.WriteLine("2: " + item); }
        // print
        // 2: Start2.MessageHandlerXY
        // 2: Start2.MessageHandlerZZ
    }

    ///<summary>Read all classes in an assembly that implement an interface (generic, or not generic)</summary>
    //
    // some references
    // return all types implementing an interface
    // https://mcmap.net/q/63745/-getting-all-types-that-implement-an-interface/12602220#12602220
    // http://haacked.com/archive/2012/07/23/get-all-types-in-an-assembly.aspx/
    // https://mcmap.net/q/64886/-how-to-prevent-reflectiontypeloadexception-when-calling-assembly-gettypes
    // return all types implementing a generic interface
    // https://mcmap.net/q/245044/-find-all-types-implementing-a-certain-generic-interface-with-specific-t-type
    // https://mcmap.net/q/243775/-get-all-types-implementing-specific-open-generic-type
    // https://mcmap.net/q/245045/-finding-out-if-a-type-implements-a-generic-interface
    // https://mcmap.net/q/245046/-net-getting-all-implementations-of-a-generic-interface
    public static Tuple<bool, IList<Type>> GetClassesImplementingAnInterface(Assembly assemblyToScan, Type implementedInterface)
    {
        if (assemblyToScan == null)
            return Tuple.Create(false, (IList<Type>)null);

        if (implementedInterface == null || !implementedInterface.IsInterface)
            return Tuple.Create(false, (IList<Type>)null);

        IEnumerable<Type> typesInTheAssembly;

        try
        {
            typesInTheAssembly = assemblyToScan.GetTypes();
        }
        catch (ReflectionTypeLoadException e)
        {
            typesInTheAssembly = e.Types.Where(t => t != null);
        }

        IList<Type> classesImplementingInterface = new List<Type>();

        // if the interface is a generic interface
        if (implementedInterface.IsGenericType)
        {
            foreach (var typeInTheAssembly in typesInTheAssembly)
            {
                if (typeInTheAssembly.IsClass)
                {
                    var typeInterfaces = typeInTheAssembly.GetInterfaces();
                    foreach (var typeInterface in typeInterfaces)
                    {
                        if (typeInterface.IsGenericType)
                        {
                            var typeGenericInterface = typeInterface.GetGenericTypeDefinition();
                            var implementedGenericInterface = implementedInterface.GetGenericTypeDefinition();

                            if (typeGenericInterface == implementedGenericInterface)
                            {
                                classesImplementingInterface.Add(typeInTheAssembly);
                            }
                        }
                    }
                }
            }
        }
        else
        {
            foreach (var typeInTheAssembly in typesInTheAssembly)
            {
                if (typeInTheAssembly.IsClass)
                {
                    // if the interface is a non-generic interface
                    if (implementedInterface.IsAssignableFrom(typeInTheAssembly))
                    {
                        classesImplementingInterface.Add(typeInTheAssembly);
                    }
                }
            }
        }
        return Tuple.Create(true, classesImplementingInterface);
    }
}

public class DummyClass
{
}

public interface IHandleMessageG<T>
{
}

public interface IHandleMessage
{
}

public interface IMsgXX
{
}

public interface IMsgXY
{
}

public interface IMsgZZ
{
}

public class MessageHandlerXY : IHandleMessageG<IMsgXY>, IHandleMessage, IMsgXX
{
    public string Handle(string a)
    {
        return "aaa";
    }
}

public class MessageHandlerZZ : IHandleMessageG<IMsgZZ>, IHandleMessage
{
    public string Handle(string a)
    {
        return "bbb";
    }
}
Consuela answered 28/9, 2016 at 10:4 Comment(1)
awesome job. works in dotnet 2.2. just suggest changing assemblyToScan from single to an array of params params Assembly[] assembliesEada
E
7

You could try

openGenericType.IsAssignableFrom(myType.GetGenericTypeDefinition()) 

or

myType.GetInterfaces().Any(i => i.GetGenericTypeDefinition() = openGenericType)
Esmaria answered 27/12, 2011 at 14:12 Comment(0)
P
6

You can use the following code to get all types that implement IRepository<> interface:

List<Type> typesImplementingIRepository = new List<Type>();
IEnumerable<Type> allTypesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes();

foreach (Type type in allTypesInThisAssembly)
{
    if (type.GetInterface(typeof(IRepository<>).Name.ToString()) != null)
    {
        typesImplementingIRepository.Add(type);
    }
}
Presumptuous answered 6/11, 2019 at 5:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.