Получение всех типов, которые реализуют интерфейс - PullRequest
496 голосов
/ 25 августа 2008

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

Вот что я хочу переписать:

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

Ответы [ 13 ]

730 голосов
/ 26 августа 2008

Мой был бы это в 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.
58 голосов
/ 26 сентября 2012

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

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }
54 голосов
/ 26 августа 2008

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

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

Обратите внимание, что предложение Райана Ринальди было неверным. Он вернет 0 типов. Вы не можете написать

where type is IFoo

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

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

46 голосов
/ 01 апреля 2015

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

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

Классическим примером невозможности активировать тип может быть случай, когда возвращаемый тип равен derived из base, но base определен в сборке, отличной от сборки 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);

Другими словами ClassB не является загружаемым , что проверяется и генерируется вызовом GetTypes.

Таким образом, чтобы безопасно квалифицировать набор результатов для загружаемых типов, в соответствии с этим Фил Хаакед статья Получить все типы в сборке и код Джона Скита вы бы вместо этого сделайте что-то вроде:

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();
}
19 голосов
/ 08 июня 2012

Другие ответы здесь используют 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();
}

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

17 голосов
/ 26 августа 2008

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

что-то вроде:

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
        }
    }
}
8 голосов
/ 12 ноября 2010

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

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 
5 голосов
/ 26 августа 2008

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

Это не красиво, я признаю.

4 голосов
/ 19 сентября 2018

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

Это так, просто замените 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();

чтобы они были в списке.

2 голосов
/ 26 августа 2008

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

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

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

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

...