Сведения о вопросе

Life

14:02, 11th August, 2020

Теги

c#   optimization   reflection   lambda   c#-3.0    

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

Просмотров: 459   Ответов: 13

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

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

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



  Сведения об ответе

DINO

10:40, 22nd August, 2020

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


  Сведения об ответе

PAGE

15:27, 1st August, 2020

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

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


  Сведения об ответе

baggs

11:39, 5th August, 2020

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

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

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

where type is IFoo

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

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


  Сведения об ответе

lool

15:26, 2nd August, 2020

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

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

Классический пример невозможности активировать тип - это когда возвращаемый тип derived из base , но base определен в другом assembly из derived, assembly, на который не ссылается вызывающий assembly.

Так скажем у нас есть:

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 получите все типы в коде Assembly и 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();
}


  Сведения об ответе

ЯЯ__4

17:39, 4th August, 2020

Другие ответы здесь используют IsAssignableFrom . Вы также можете использовать FindInterfaces из пространства имен System , как описано здесь .

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

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


  Сведения об ответе

JUST___

20:02, 21st August, 2020

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

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

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


  Сведения об ответе

VCe znayu

06:33, 2nd August, 2020

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

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


  Сведения об ответе

SILA

11:42, 4th August, 2020

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

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


  Сведения об ответе

9090

12:19, 19th August, 2020

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

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

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


  Сведения об ответе

9090

09:35, 9th August, 2020

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

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

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

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


  Сведения об ответе

PIRLO

21:06, 1st October, 2020

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

Может Билгин


  Сведения об ответе

прога

00:39, 6th August, 2020

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


  Сведения об ответе

nYU

13:07, 4th August, 2020

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

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться