获取所有实现特定开放式泛型类型的类型

75

如何获取实现特定开放泛型类型的所有类型?

例如:

public interface IUserRepository : IRepository<User>

查找所有实现IRepository<>的类型。

public static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
  ...
}
4个回答

102

这将返回所有继承泛型基类的类型,而不是继承泛型接口的类型。

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;

这将返回所有类型,包括接口、抽象类和具有开放泛型类型的继承链中的具体类型。

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;
}

这种第二种方法将在此示例中查找ConcreteUserRepoIUserRepository:

public class ConcreteUserRepo : IUserRepository
{}

public interface IUserRepository : IRepository<User>
{}

public interface IRepository<User>
{}

public class User
{}

2
是的,但只有在同一程序集中时,您才能获得concreterepo和irepo。但这完全没问题。 - Rookian
好的发现!我们必须小心加载所有依赖程序集,而不仅仅是扫描当前加载的程序集。这里有一个关于加载所有程序集的SO:https://dev59.com/questions/yXE95IYBdhLWcg3wRLst - Nick VanderPyle
如果泛型类型有单个泛型参数,则 typeof(IRepository<>) 可以正常工作。对于具有多个泛型类型参数的类型,有什么想法吗? - Sudhanshu Mishra
3
抱歉,我找到答案了。你只需要 typeof(IRepository <,>)(假设有两个通用类型参数),等等!太棒了! - Sudhanshu Mishra
2
这只有在具体类型直接继承搜索的接口时才有效,不是吗? - Landerah

9

没有使用LINQ实现的解决方案,同时搜索泛型和非泛型接口,并过滤返回类型为类。

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://dev59.com/pHVD5IYBdhLWcg3wTJrF#12602220
    // http://haacked.com/archive/2012/07/23/get-all-types-in-an-assembly.aspx/
    // https://dev59.com/gWsz5IYBdhLWcg3wcndb
    // return all types implementing a generic interface
    // http://stackoverflow.com/questions/33694960/find-all-types-implementing-a-certain-generic-interface-with-specific-t-type
    // https://dev59.com/Wmoy5IYBdhLWcg3wScPb
    // https://dev59.com/TXNA5IYBdhLWcg3wBpBm
    // https://dev59.com/52025IYBdhLWcg3wtoM_
    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";
    }
}

太棒了!可以在DotNet 2.2中运行。建议将assemblyToScan从单个参数更改为参数数组params Assembly[] assemblies - Soren

7

您可以尝试

openGenericType.IsAssignableFrom(myType.GetGenericTypeDefinition()) 

或者

myType.GetInterfaces().Any(i => i.GetGenericTypeDefinition() = openGenericType)

5

您可以使用以下代码获取实现了 IRepository<> 接口的所有类型:

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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接