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



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



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



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

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

потому что тип-это система.Тип экземпляра, и никогда не будет типа 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 но 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);

In другими словами ClassB не loadable это то,что проверяет и бросает вызов 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;

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

Comments

    Ничего не найдено.