Getting all types that implement an interface

前端 未结 17 1002
不思量自难忘°
不思量自难忘° 2020-11-22 00:34

Using reflection, how can I get all types that implement an interface with C# 3.0/.NET 3.5 with the least code, and minimizing iterations?

This is what I want to re-

相关标签:
17条回答
  • 2020-11-22 01:31

    Edit: I've just seen the edit to clarify that the original question was for the reduction of iterations / code and that's all well and good as an exercise, but in real-world situations you're going to want the fastest implementation, regardless of how cool the underlying LINQ looks.

    Here's my Utils method for iterating through the loaded types. It handles regular classes as well as interfaces, and the excludeSystemTypes option speeds things up hugely if you are looking for implementations in your own / third-party codebase.

    public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) {
        List<Type> list = new List<Type>();
        IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
        while (enumerator.MoveNext()) {
            try {
                Type[] types = ((Assembly) enumerator.Current).GetTypes();
                if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) {
                    IEnumerator enumerator2 = types.GetEnumerator();
                    while (enumerator2.MoveNext()) {
                        Type current = (Type) enumerator2.Current;
                        if (type.IsInterface) {
                            if (current.GetInterface(type.FullName) != null) {
                                list.Add(current);
                            }
                        } else if (current.IsSubclassOf(type)) {
                            list.Add(current);
                        }
                    }
                }
            } catch {
            }
        }
        return list;
    }
    

    It's not pretty, I'll admit.

    0 讨论(0)
  • 2020-11-22 01:32

    Other answers here use IsAssignableFrom. You can also use FindInterfaces from the System namespace, as described here.

    Here's an example that checks all assemblies in the currently executing assembly's folder, looking for classes that implement a certain interface (avoiding LINQ for clarity).

    static void Main() {
        const string qualifiedInterfaceName = "Interfaces.IMyInterface";
        var interfaceFilter = new TypeFilter(InterfaceFilter);
        var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        var di = new DirectoryInfo(path);
        foreach (var file in di.GetFiles("*.dll")) {
            try {
                var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
                foreach (var type in nextAssembly.GetTypes()) {
                    var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
                    if (myInterfaces.Length > 0) {
                        // This class implements the interface
                    }
                }
            } catch (BadImageFormatException) {
                // Not a .net assembly  - ignore
            }
        }
    }
    
    public static bool InterfaceFilter(Type typeObj, Object criteriaObj) {
        return typeObj.ToString() == criteriaObj.ToString();
    }
    

    You can set up a list of interfaces if you want to match more than one.

    0 讨论(0)
  • 2020-11-22 01:34
       public IList<T> GetClassByType<T>()
       {
            return AppDomain.CurrentDomain.GetAssemblies()
                              .SelectMany(s => s.GetTypes())
                              .ToList(p => typeof(T)
                              .IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
                              .SelectList(c => (T)Activator.CreateInstance(c));
       }
    
    0 讨论(0)
  • 2020-11-22 01:36

    I see so many overcomplicated answers here and people always tell me that I tend to overcomplicate things. Also using IsAssignableFrom method for the purpose of solving OP problem is wrong!

    Here is my example, it selects all assemblies from the app domain, then it takes flat list of all available types and checks every single type's list of interfaces for match:

    public static IEnumerable<Type> GetImplementingTypes(this Type itype) 
        => AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
               .Where(t => t.GetInterfaces().Contains(itype));
    
    0 讨论(0)
  • 2020-11-22 01:39

    I appreciate this is a very old question but I thought I would add another answer for future users as all the answers to date use some form of Assembly.GetTypes.

    Whilst GetTypes() will indeed return all types, it does not necessarily mean you could activate them and could thus potentially throw a ReflectionTypeLoadException.

    A classic example for not being able to activate a type would be when the type returned is derived from base but base is defined in a different assembly from that of derived, an assembly that the calling assembly does not reference.

    So say we have:

    Class A // in AssemblyA
    Class B : Class A, IMyInterface // in AssemblyB
    Class C // in AssemblyC which references AssemblyB but not AssemblyA
    

    If in ClassC which is in AssemblyC we then do something as per accepted answer:

    var type = typeof(IMyInterface);
    var types = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(s => s.GetTypes())
        .Where(p => type.IsAssignableFrom(p));
    

    Then it will throw a ReflectionTypeLoadException.

    This is because without a reference to AssemblyA in AssemblyC you would not be able to:

    var bType = typeof(ClassB);
    var bClass = (ClassB)Activator.CreateInstance(bType);
    

    In other words ClassB is not loadable which is something that the call to GetTypes checks and throws on.

    So to safely qualify the result set for loadable types then as per this Phil Haacked article Get All Types in an Assembly and Jon Skeet code you would instead do something like:

    public static class TypeLoaderExtensions {
        public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
            if (assembly == null) throw new ArgumentNullException("assembly");
            try {
                return assembly.GetTypes();
            } catch (ReflectionTypeLoadException e) {
                return e.Types.Where(t => t != null);
            }
        }
    }
    

    And then:

    private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
        var it = typeof (IMyInterface);
        return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
    }
    
    0 讨论(0)
提交回复
热议问题