662 Stimmen

Alle Typen ermitteln, die eine Schnittstelle implementieren

Wie kann ich unter Verwendung von Reflection alle Typen, die eine Schnittstelle implementieren, mit C# 3.0/.NET 3.5 mit so wenig Code wie möglich abrufen und Iterationen minimieren?

Das möchte ich neu schreiben:

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

946voto

Darren Kopp Punkte 74401

Meine wäre dies in c# 3.0 :)

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

Im Grunde genommen wird die geringste Anzahl von Iterationen immer sein:

loop assemblies  
 loop types  
  see if implemented.

95voto

Ben Watkins Punkte 867

Das hat bei mir funktioniert. Es führt eine Schleife durch 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
 }

84voto

rism Punkte 11523

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 eine Form von Assembly.GetTypes .

Obwohl GetTypes() tatsächlich alle Typen zurückgibt, bedeutet dies nicht unbedingt, dass Sie sie aktivieren können und somit möglicherweise ein ReflectionTypeLoadException .

Ein klassisches Beispiel dafür, dass ein Typ nicht aktiviert werden kann, wäre, wenn der zurückgegebene Typ derived von base sondern base in einer anderen Baugruppe definiert ist als die von derived eine Assembly, die von der aufrufenden Assembly nicht referenziert wird.

Sagen wir, wir haben:

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

Wenn in ClassC die sich in AssemblyC tun wir 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 eine ReflectionTypeLoadException .

Denn ohne einen Verweis auf AssemblyA in AssemblyC wären Sie dazu nicht in der Lage:

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

Mit anderen Worten ClassB ist nicht ladbar was der Aufruf von GetTypes prüft und auslöst.

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

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

66voto

Um alle Typen in einer Assembly zu finden, 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. Er wird 0 Typen zurückgeben. Sie können 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. Das führt zu den erwarteten Ergebnissen.

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

24voto

hillstuk Punkte 1446

Andere Antworten hier verwenden IsAssignableFrom . Sie können auch verwenden FindInterfaces von der System Namespace, wie beschrieben ici .

Hier ist ein Beispiel, das alle Assemblies im Ordner der aktuell ausgeführten Assembly überprüft und nach Klassen sucht, die eine bestimmte Schnittstelle implementieren (der Übersichtlichkeit halber 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 erstellen, wenn Sie mehr als eine Schnittstelle abgleichen möchten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X