Вопрос

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

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

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
Это было полезно?

Решение

Моим было бы это в 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.

Другие советы

У меня это сработало.Он перебирает классы и проверяет, получены ли они из MyInterface

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

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

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

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

where type is IFoo

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

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

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

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

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

Признаю, это некрасиво.

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

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

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

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

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

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

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

Еще лучше при выборе места сборки.Отфильтруйте большинство сборок, если вы знаете, что все ваши реализованные интерфейсы находятся в пределах одной сборки.DefinedTypes.

// We get the assembly through the base class
var baseAssembly = typeof(baseClass).GetTypeInfo().Assembly;

// we filter the defined classes according to the interfaces they implement
var typeList = baseAssembly.DefinedTypes.Where(type => type.ImplementedInterfaces.Any(inter => inter == typeof(IMyInterface))).ToList();

Автор : Кан Билгин

Я получил исключения в 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;
}

Вы могли бы использовать какой-нибудь LINQ для получения списка:

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

Но на самом деле, разве это более читабельно?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top