Подтвердить что ты не робот

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

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

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

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

Ответ 1

Mine будет в С# 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.

Ответ 2

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

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

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

where type is IFoo

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

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

Ответ 3

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

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

Ответ 4

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

Итак, чтобы безопасно квалифицировать набор результатов для загружаемых типов, то согласно этому Phil Haacked article Получить все типы в сборке и код Jon Skeet, вы бы сделали что-то вроде:

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

Ответ 5

В других ответах здесь используется 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();
}

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

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

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

иметь их в списке.

Ответ 10

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

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

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

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

Ответ 11

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

Ответ 12

Еще лучше при выборе места сборки. Отфильтруйте большинство сборок, если вы знаете, что все ваши реализованные интерфейсы находятся в одном и том же Assembly.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();

Кан Билгин

Ответ 13

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

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

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