Получение всех типов, реализующих интерфейс

используя отражение, как я могу получить все типы, которые реализуют интерфейс с C# 3.0 / .NET 3.5 с наименьшим кодом и минимизацией итераций?

Это то, что я хочу заново написать:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff

12 ответов


мой будет это в c# 3.0:)

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

в принципе, наименьшее количество итераций всегда будет:

loop assemblies  
 loop types  
  see if implemented.

чтобы найти все типы в сборке, которые реализуют интерфейс IFoo:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;
where type is IFoo

потому что type-это система.Введите экземпляр и никогда не будет иметь тип IFoo. Вместо этого вы проверяете, можно ли назначить IFoo из типа. Это даст ожидаемые результаты.

кроме того, предложение Адама Райта, которое в настоящее время отмечено как ответ, неверно, как ну и по той же причине. Во время выполнения вы увидите, что 0 типов возвращаются, потому что вся система.Экземпляры типов не были реализаторами IFoo.


это сработало для меня. Это петли, хотя классы и проверяет, чтобы увидеть, если они которые можно вывести из myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }

я ценю, что это очень старый вопрос, но я думал, что добавлю еще один ответ для будущих пользователей, поскольку все ответы на сегодняшний день используют некоторую форму Assembly.GetTypes.

в то время как GetTypes() действительно вернет все типы, это не обязательно означает, что вы можете активировать их и, таким образом, потенциально бросить ReflectionTypeLoadException.

классический пример невозможности активировать тип будет, когда тип возвращается derived С base но!--8--> определен в другой сборке от derived, сборка, на которую вызывающая сборка не ссылается.

так скажем у нас:

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

если в ClassC который находится в AssemblyC затем мы делаем что-то в соответствии с принятым ответом:

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

тогда он бросит ReflectionTypeLoadException.

это потому что без ссылки на AssemblyA на AssemblyC вы не сможете:

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

In другие слова ClassB не загружаемый который является то, что вызов GetTypes проверяет и бросает на.

так смело претендовать результирующий набор подгружаемых типов, тогда как за этот Фил Haacked статьи получить все типы в сборке и Джон Скит код вы бы вместо этого сделать что-то вроде:

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

и затем:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}

другие ответы здесь использовать IsAssignableFrom. Вы также можете использовать FindInterfaces С System пространства имен, как описано здесь.

вот пример, который проверяет все сборки в папке текущей выполняемой сборки, ища классы, реализующие определенный интерфейс (избегая LINQ для ясности).

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

вы можете настроить список интерфейсов, если хотите сопоставить более одного.


цикл через все загруженные сборки, цикл через все их типы и проверьте, реализуют ли они интерфейс.

что-то типа:

Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
    foreach (Type t in asm.GetTypes()) {
        if (ti.IsAssignableFrom(t)) {
            // here's your type in t
        }
    }
}

это сработало для меня (если вы хотите, чтобы вы могли исключить системные типы в поиске):

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 

Edit: я только что видел редактирование, чтобы уточнить, что исходный вопрос был для сокращения итераций / кода, и это все хорошо и хорошо как упражнение, но в реальных ситуациях вам понадобится самая быстрая реализация, независимо от того, насколько круто выглядит базовый LINQ.

вот мой метод Utils для итерации через загруженные типы. Он обрабатывает регулярные классы, а также интерфейсы, и опция excludeSystemTypes значительно ускоряет работу, если вы поиск реализаций в вашей собственной / сторонней кодовой базе.

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

нет простого способа (с точки зрения производительности) сделать то, что вы хотите сделать.

отражение работает с ассемблерами и типами в основном, поэтому вам придется получить все типы сборки и запросить их для правильного интерфейса. Вот пример:

Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);

Это даст вам все типы, которые реализуют IMyInterface в сборке MyAssembly


У меня есть исключения в linq-коде, поэтому я делаю это так (без сложного расширения):

private static IList<Type> loadAllTypes(Types[] interfaces)
{
    IList<Type> objects = new List<Type>();

    // find all types
    foreach (var interfaceType in interfaces)
        foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies())
            try
            {
                foreach (var currentType in currentAsm.GetTypes())
                    if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract)
                        objects.Add(currentType);
            }
            catch { }

    return objects;
}

другой ответ не работал с универсальный интерфейс.

это так, просто замените typeof (ISomeInterface) на typeof (T).

List<string> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
            .Where(x => typeof(ISomeInterface).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
            .Select(x => x.Name).ToList();

поэтому с

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

мы получаем все сборки

!x.IsInterface && !x.IsAbstract

используется для исключения интерфейса и абстрактных и

.Select(x => x.Name).ToList();

чтобы иметь их в список.


вы можете использовать LINQ для получения списка:

var types = from type in this.GetType().Assembly.GetTypes()
            where type is ISomeInterface
            select type;

но на самом деле, это более читабельным?