Frage

Wie kann ich mithilfe von Reflektion alle Typen erhalten, die eine Schnittstelle mit C# 3.0/.NET 3.5 mit dem geringsten Code und minimalen Iterationen implementieren?

Folgendes möchte ich umschreiben:

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

Lösung

Meins wäre das in c# 3.0 :)

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

Grundsätzlich beträgt die geringste Anzahl an Iterationen immer:

loop assemblies  
 loop types  
  see if implemented.

Andere Tipps

Das hat bei mir funktioniert.Es durchläuft die Klassen und prüft, ob sie von myInterface abgeleitet sind

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

So finden Sie alle Typen in einer Assembly, die die IFoo-Schnittstelle implementieren:

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

Beachten Sie, dass der Vorschlag von Ryan Rinaldi falsch war.Es werden 0 Typen zurückgegeben.Du kannst nicht schreiben

where type is IFoo

weil type eine System.Type-Instanz ist und niemals vom Typ IFoo sein wird.Stattdessen prüfen Sie, ob IFoo vom Typ zuweisbar ist.Dadurch werden die erwarteten Ergebnisse erzielt.

Auch der Vorschlag von Adam Wright, der derzeit als Antwort markiert ist, ist aus demselben Grund falsch.Zur Laufzeit werden 0 Typen zurückkommen, da alle System.Type-Instanzen keine IFoo-Implementierer waren.

Ich weiß, dass dies eine sehr alte Frage ist, aber ich dachte, ich würde eine weitere Antwort für zukünftige Benutzer hinzufügen, da alle bisherigen Antworten irgendeine Form von verwenden Assembly.GetTypes.

Während GetTypes() tatsächlich alle Typen zurückgibt, bedeutet dies nicht unbedingt, dass Sie sie aktivieren und somit möglicherweise einen auslösen könnten ReflectionTypeLoadException.

Ein klassisches Beispiel dafür, dass ein Typ nicht aktiviert werden kann, wäre, wenn der Typ zurückgegeben wird derived aus base Aber base ist in einer anderen Assembly als der von definiert derived, eine Assembly, auf die die aufrufende Assembly nicht verweist.

Angenommen, wir haben:

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

Wenn drin ClassC welches ist in AssemblyC Wir machen dann etwas gemäß der akzeptierten Antwort:

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

Dann wird es einen werfen ReflectionTypeLoadException.

Dies liegt daran, dass ohne Verweis auf AssemblyA In AssemblyC Sie wären nicht in der Lage:

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

Mit anderen Worten ClassB ist nicht ladbar Das ist etwas, das der Aufruf von GetTypes prüft und auslöst.

Um die Ergebnismenge also sicher für ladbare Typen zu qualifizieren, gehen Sie wie folgt vor Phil Haacked Artikel Alle Typen in einer Baugruppe abrufen Und Jon Skeet-Code Sie würden stattdessen so etwas tun:

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

Und dann:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}

Andere Antworten hier verwenden IsAssignableFrom.Sie können auch verwenden FindInterfaces von dem System Namensraum, wie beschrieben Hier.

Hier ist ein Beispiel, das alle Assemblys im Ordner der aktuell ausgeführten Assembly überprüft und nach Klassen sucht, die eine bestimmte Schnittstelle implementieren (aus Gründen der Übersichtlichkeit wird LINQ vermieden).

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

Sie können eine Liste von Schnittstellen einrichten, wenn Sie mehr als eine abgleichen möchten.

Durchlaufen Sie alle geladenen Assemblys, durchlaufen Sie alle ihre Typen und prüfen Sie, ob sie die Schnittstelle implementieren.

etwas wie:

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

Das hat bei mir funktioniert (wenn Sie möchten, können Sie Systemtypen bei der Suche ausschließen):

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

Bearbeiten:Ich habe gerade die Änderung gesehen, um klarzustellen, dass die ursprüngliche Frage die Reduzierung von Iterationen/Code betraf und das als Übung schön und gut ist, aber in realen Situationen möchte man die schnellste Implementierung, egal wie Cool, das zugrunde liegende LINQ sieht aus.

Hier ist meine Utils-Methode zum Durchlaufen der geladenen Typen.Es verarbeitet sowohl reguläre Klassen als auch Schnittstellen, und die Option „excludeSystemTypes“ beschleunigt die Arbeit erheblich, wenn Sie nach Implementierungen in Ihrer eigenen Codebasis bzw. in der Codebasis eines Drittanbieters suchen.

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

Es ist nicht schön, das gebe ich zu.

Andere Antworten funktionierten nicht mit a generische Schnittstelle.

In diesem Fall ersetzen Sie einfach typeof(ISomeInterface) durch 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();

Also mit

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

Wir bekommen alle Baugruppen

!x.IsInterface && !x.IsAbstract

wird verwendet, um die Schnittstelle und die abstrakten auszuschließen und

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

um sie in einer Liste zu haben.

Es gibt keine einfache Möglichkeit (in Bezug auf die Leistung), das zu tun, was Sie tun möchten.

Reflection arbeitet hauptsächlich mit Assemblys und Typen, sodass Sie alle Typen der Assembly abrufen und sie nach der richtigen Schnittstelle abfragen müssen.Hier ist ein Beispiel:

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

Dadurch erhalten Sie alle Typen, die IMyInterface in der Assembly MyAssembly implementieren

Noch besser bei der Wahl des Montageortes.Filtern Sie die meisten Assemblys, wenn Sie wissen, dass sich alle implementierten Schnittstellen innerhalb derselben Assembly.DefinedTypes befinden.

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

Von Can Bilgin

Ich habe Ausnahmen im Linq-Code, also mache ich es so (ohne komplizierte Erweiterung):

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

Sie könnten etwas LINQ verwenden, um die Liste zu erhalten:

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

Aber ist das wirklich besser lesbar?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top