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


4 Answers

Чтобы найти все типы в сборке, которые реализуют интерфейс 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.

Question

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

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

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



У меня есть исключения в 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;
}



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

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

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



Изменить: я только что видел редактирование, чтобы уточнить, что исходный вопрос был для сокращения итераций / кода, и все это хорошо и хорошо, как упражнение, но в реальных ситуациях вам понадобится самая быстрая реализация, независимо как классно выглядит 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.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();
}





Related