Uzyskanie wszystkich typów implementujących interfejs

Używając reflection, jak Mogę uzyskać wszystkie typy, które implementują interfejs z C# 3.0/. NET 3.5 z najmniejszym kodem i minimalizując iteracje?

Oto co chcę napisać od nowa:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
Author: Ondrej Janacek, 2008-08-25

12 answers

Mój byłby taki w c # 3.0:)

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

W zasadzie najmniejszą liczbą iteracji zawsze będzie:

loop assemblies  
 loop types  
  see if implemented.
 669
Author: Darren Kopp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-06 18:41:47

Aby znaleźć wszystkie typy w zestawie, które implementują interfejs IFoo:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;
Zauważ, że sugestia Ryana Rinaldiego była błędna. Zwróci 0 typów. Nie możesz napisać
where type is IFoo

Ponieważ typ jest systemem.Type instance, i nigdy nie będzie typu IFoo. Zamiast tego sprawdzasz, czy IFoo można przypisać z tego typu. To przyniesie oczekiwane rezultaty.

Również sugestia Adama Wrighta, która jest obecnie oznaczona jako odpowiedź, jest niepoprawna, a dla tego samego powód. W czasie wykonywania, zobaczysz 0 typów wrócić, ponieważ wszystkie System.Instancje typu nie były implementatorami IFoo.
 53
Author: Judah Himango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 20:44:18

To mi pomogło. Zapętla klasy i sprawdza, czy są one derrived z myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }
 48
Author: Ben Watkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 15:06:02

Doceniam, że to bardzo stare pytanie, ale pomyślałem, że dodam kolejną odpowiedź dla przyszłych użytkowników, ponieważ wszystkie odpowiedzi do tej pory używają jakiejś formy Assembly.GetTypes.

Podczas gdy GetTypes() rzeczywiście zwróci wszystkie typy, nie musi to oznaczać, że możesz je aktywować i potencjalnie rzucić ReflectionTypeLoadException.

Klasycznym przykładem na to, że nie można uaktywnić typu jest, gdy typ zwracany jest derived z base, Ale base jest zdefiniowany w innym assembly od derived, assembly, do którego nie odwołuje się wywołujący assembly.

Więc powiedzmy, że mamy:

Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA

Jeśli w ClassC które jest w {[12] } wtedy robimy coś zgodnie z przyjętą odpowiedzią:

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

Wtedy rzuci ReflectionTypeLoadException.

To dlatego, że bez odniesienia do AssemblyA W AssemblyC nie byłbyś w stanie:

var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);

Innymi słowy ClassB nie jest ładowane co jest czymś, co wywołanie GetTypes sprawdza i rzuca.

Więc aby bezpiecznie zakwalifikować zestaw wyników dla typów ładowanych, to zgodnie z tym Phil Haacked artykuł Pobierz wszystkie typy W Zestawie i Jon Skeet code zamiast tego zrobisz coś w stylu:

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

A potem:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
 42
Author: rism,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

Inne odpowiedzi tutaj użyć IsAssignableFrom. Możesz również użyć FindInterfaces z przestrzeni nazw System, jak opisano tutaj.

Oto przykład, który sprawdza wszystkie złożenia w aktualnie wykonującym folderze assembly, szukając klas implementujących określony interfejs(unikając LINQ dla przejrzystości).

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

Możesz skonfigurować listę interfejsów, jeśli chcesz dopasować więcej niż jeden.

 18
Author: hillstuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-07 12:19:49

Pętla przez wszystkie załadowane zespoły, Pętla przez wszystkie ich typy i sprawdzić, czy zaimplementowali interfejs.

Coś w stylu:

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
        }
    }
}
 13
Author: Lasse Vågsæther Karlsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 12:40:06

To zadziałało dla mnie (jeśli chcesz, możesz wykluczyć typy systemów w wyszukiwarce):

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 
 7
Author: Carl Nayak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-29 09:55:20

Edit: właśnie widziałem edycję, aby wyjaśnić, że pierwotne pytanie dotyczyło redukcji iteracji / kodu i to wszystko dobrze i dobrze jako ćwiczenie, ale w rzeczywistych sytuacjach będziesz chciał najszybszej implementacji, niezależnie od tego, jak fajnie wygląda LINQ.

Oto moja metoda Utils do iteracji przez załadowane typy. Obsługuje zarówno zwykłe klasy, jak i interfejsy, a opcja excludeSystemTypes znacznie przyspiesza działanie, jeśli szukasz implementacje we własnej / zewnętrznej bazie kodowej.

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;
}
Przyznam, że nie jest ładna.
 5
Author: tags2k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 12:40:53

Nie ma łatwego sposobu (jeśli chodzi o wydajność), aby zrobić to, co chcesz zrobić.

Reflection działa głównie z assembly i typami, więc będziesz musiał pobrać wszystkie typy assembly i odpytywać je o odpowiedni interfejs. Oto przykład:

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

Dzięki temu uzyskasz wszystkie typy implementujące interfejs IMyInterface w złożeniu MyAssembly

 2
Author: Jorge Córdoba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 20:13:17

Mam wyjątki w kodzie linq więc robię to w ten sposób (bez skomplikowanego rozszerzenia):

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;
}
 0
Author: user6537157,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-27 10:47:08

Inne odpowiedzi nie działały z ogólnym interfejsem .

Ten robi, wystarczy zastąpić typeof (ISomeInterface) przez 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();

Więc z

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

Otrzymujemy wszystkie zespoły

!x.IsInterface && !x.IsAbstract

Jest używany do wykluczenia interfejsu i abstrakcyjnych oraz

.Select(x => x.Name).ToList();

Mieć je na liście.

 0
Author: 421,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 08:27:36

Przydałby się jakiś LINQ żeby dostać listę:

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

Ale naprawdę, czy to jest bardziej czytelne?

 -3
Author: Ryan Rinaldi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 20:18:59