Abrufen aller Typen, die eine Schnittstelle implementieren

553

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

Folgendes möchte ich neu schreiben:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
Juan
quelle
1
Funktioniert der Beispielcode? Ich habe falsche Negative mit Ihrer if-Bedingung.
Kaiser Orionii
3
Die if-Anweisung im obigen Code ist immer falsch, da Sie testen, ob eine Instanz der Type-Klasse (t) Ihre Schnittstelle implementiert, was nicht der Fall ist, es sei denn, Type erbt IMyInterface (in diesem Fall ist sie immer true).
Liazy

Antworten:

807

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 ist die geringste Anzahl von Iterationen immer:

loop assemblies  
 loop types  
  see if implemented.
Darren Kopp
quelle
194
Beachten Sie, dass die Liste möglicherweise auch die Schnittstelle selbst enthält. Ändern Sie die letzte Zeile in .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);, um sie herauszufiltern (oder p.IsClass).
jtpereyda
39
Hinweis: Diese Antwort ist falsch! Hiermit wird "Zuweisungskompatibilität" überprüft, nicht ob die Schnittstelle implementiert ist. Zum Beispiel List<string>nicht implementiert, IEnumerable<object>aber diese Methode gibt in .Net 4.0 aufgrund der tatsächlich falschen Kovarianz true zurück. Die richtige Antwort ist hier
Sriram Sakthivel
20
@SriramSakthivel Zunächst einmal wurden keine generischen Werte angegeben. Zweitens geht diese Frage der Kovarianz voraus. Drittens gehen Sie davon aus, dass die kovariante Rendite nicht das ist, was sie wollen.
Darren Kopp
24
Du hast absolut Recht, Darren, ich weiß, dass dies ein alter Thread ist. Ich habe meinen Kommentar nur registriert, damit die zukünftigen Benutzer auf ein solches Problem aufmerksam werden. Dich nicht zu beleidigen. und wie der Fragentitel besagt, wenn OP nach dem Abrufen aller Typen fragt , die eine Schnittstelle implementieren, tut dieser Code dies nicht. aber in fast allen Fällen funktioniert es zweifellos. Es gibt auch Eckfälle, wie ich sagte. Nur um sich dessen bewusst zu sein;
Sriram Sakthivel
9
.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Muss
66

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
 }
Ben Watkins
quelle
5
Sie gehen davon aus, dass sich die Assembly in der ausführbaren Hauptdatei befindet. Kein zusätzliches Projekt. Sie iterieren auch unnötig durch eine Reihe von Iterationen. Es ist besser, wenn der Rahmen das schwere Heben übernimmt. Filtern Sie dann weiter nach unten, wenn Sie gefunden werden. Falls relevant, aktualisieren Sie bitte Ihre Antwort. Include List <T> Argumentation. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Where (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces (mytype) )); foreach (var item in items) Console.Log (item.Name);
TamusJRoyce
58

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 überprüfen Sie, ob IFoo vom Typ zuweisbar ist. Das wird Ihre erwarteten Ergebnisse erhalten.

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

Judah Gabriel Himango
quelle
58

Ich schätze, 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.

GetTypes () gibt zwar alle Typen zurück, bedeutet jedoch nicht unbedingt, dass Sie sie aktivieren und somit möglicherweise a auslösen können ReflectionTypeLoadException.

Ein klassisches Beispiel dafür, dass ein Typ nicht aktiviert werden kann, ist, wenn der zurückgegebene Typ derivedvon stammt, baseaber basein einer anderen Assembly als der von derivedeiner Assembly definiert ist, auf die die aufrufende Assembly nicht verweist.

Sagen wir also, 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 ClassCwas AssemblyCdrin ist, dann tun wir 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 ein werfen ReflectionTypeLoadException.

Dies liegt daran, dass Sie ohne einen Verweis auf AssemblyA in AssemblyCnicht in der Lage wären:

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

Mit anderen Worten ClassBist nicht ladbar das ist etwas, dass der Aufruf von GetTypes Kontrollen und wirft auf.

Um die Ergebnismenge sicher für ladbare Typen zu qualifizieren, führen Sie stattdessen gemäß diesem Phil Haacked- Artikel Get All Types in a Assembly und Jon Skeet-Code Folgendes aus :

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();
}
Risma
quelle
3
Dies hat mir geholfen, mit einem super seltsamen Problem umzugehen, bei dem GetTypes in meinem Testprojekt fehlschlagen würde und nur in unserer CI-Umgebung. GetLoadableTypes war ein Fix für diese Lösung. Der Fehler wäre in der lokalen Umgebung nicht reproduzierbar und lautete wie folgt: System.Reflection.ReflectionTypeLoadException: Einer oder mehrere der angeforderten Typen können nicht geladen werden. Rufen Sie die LoaderExceptions-Eigenschaft ab, um weitere Informationen zu erhalten. Insbesondere wurde beanstandet, dass es einen Typ gab, der keine konkrete Implementierung hatte, und dies geschah im Unit-Test-Projekt. Danke dafür!
Lari Tuomisto
2
Diese Antwort sollte als Lösung markiert werden, sie hat mir heute den Arsch gerettet, denn wie @Lari Tuomisto sagte, konnten wir auf lokaler Umgebung keinen ähnlichen Fehler nachproduzieren
Lightning3
3
Falls es jemand anderem hilft: Diese Lösung hat bei mir funktioniert, aber ich musste sie ändern, um den Schnittstellentyp aus der Liste zu entfernen. Ich wollte CreateInstancefür alle aktivieren , und es wurde eine Ausnahme ausgelöst, als versucht wurde, die eigentliche Schnittstelle zu erstellen (was mich eine Weile verwirrt hatte, als ich dachte, dass die eigentliche Schnittstelle in dieser Lösung nicht im Weg war). Also habe ich den Code in geändert GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña
21

Andere Antworten hier verwenden IsAssignableFrom. Sie können auch FindInterfacesaus dem SystemNamespace verwenden, wie hier beschrieben .

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 (wobei LINQ aus Gründen der Übersichtlichkeit vermieden wird).

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 übereinstimmen möchten.

Hillstuk
quelle
Dieser sucht nach dem Namen der String-Schnittstelle, nach dem ich gesucht habe.
Senthil
Funktioniert beim Laden einer Assembly in einer anderen Domäne, da der Typ in eine Zeichenfolge serialisiert werden muss. sehr beeindruckend!
TamusJRoyce
Ich erhalte: Die Abhängigkeit von der Assembly 'System.Core, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089' kann nicht aufgelöst werden, da sie nicht vorinstalliert wurde. Bei Verwendung der ReflectionOnly-APIs müssen abhängige Assemblys bei Bedarf über das ReflectionOnlyAssemblyResolve-Ereignis vorinstalliert oder geladen werden.
bkwdesign
18

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
        }
    }
}
Lasse V. Karlsen
quelle
8

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

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

Bearbeiten: Ich habe gerade die Bearbeitung gesehen, um zu verdeutlichen, dass die ursprüngliche Frage die Reduzierung von Iterationen / Code betraf und das alles gut und gut als Übung ist, aber in realen Situationen werden Sie unabhängig davon die schnellste Implementierung wünschen wie cool der zugrunde liegende LINQ aussieht.

Hier ist meine Utils-Methode zum Durchlaufen der geladenen Typen. Es behandelt sowohl reguläre Klassen als auch Schnittstellen, und die Option excludeSystemTypes beschleunigt die Arbeit erheblich, wenn Sie nach Implementierungen in Ihrer eigenen Codebasis / Drittanbieter-Codebasis 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, gebe ich zu.

tags2k
quelle
2
Enumeratoren implementieren IDisposable, das nicht in einem Versuch / Endlich entsorgt wird. Es ist besser, ein foreach oder linq zu verwenden.
TamusJRoyce
Warum testest du excludeSystemTypeszweimal in einem if?
NetMage
4

Andere Antworten funktionierten nicht mit einer generischen 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 abstrakte auszuschließen und

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

um sie in einer Liste zu haben.

Antonin GAVREL
quelle
Bitte erläutern Sie, wie Ihre Lösung funktioniert und warum sie allen anderen Antworten überlegen ist.
Lukas Körfer
Es ist nicht überlegen oder niedriger, die anderen Antworten haben bei mir nicht funktioniert und ich habe mir die Mühe gemacht, sie zu teilen.
Antonin GAVREL
In meinem Kommentar ging es nur darum, dass Ihre Antwort nur Code ist. Deshalb habe ich Sie gebeten, eine Erklärung hinzuzufügen.
Lukas Körfer
2

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

Reflection funktioniert hauptsächlich mit Baugruppen und Typen, sodass Sie alle Baugruppentypen abrufen und 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);

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

Jorge Córdoba
quelle
2

Noch besser bei der Auswahl des Montageorts. Filtern Sie die meisten Assemblys, wenn Sie wissen, dass sich alle implementierten Schnittstellen in derselben Assembly befinden. 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();

Von Can Bilgin

user489566
quelle
1

Es gibt bereits viele gültige Antworten, aber ich möchte eine weitere Implementierung als Typerweiterung und eine Liste von Komponententests hinzufügen, um verschiedene Szenarien zu demonstrieren:

public static class TypeExtensions
{
    public static IEnumerable<Type> GetAllTypes(this Type type)
    {
        var typeInfo = type.GetTypeInfo();
        var allTypes = GetAllImplementedTypes(type).Concat(typeInfo.ImplementedInterfaces);
        return allTypes;
    }

    private static IEnumerable<Type> GetAllImplementedTypes(Type type)
    {
        yield return type;
        var typeInfo = type.GetTypeInfo();
        var baseType = typeInfo.BaseType;
        if (baseType != null)
        {
            foreach (var foundType in GetAllImplementedTypes(baseType))
            {
                yield return foundType;
            }
        }
    }
}

Dieser Algorithmus unterstützt die folgenden Szenarien:

public static class GetAllTypesTests
{
    public class Given_A_Sample_Standalone_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleStandalone);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleStandalone),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Abstract_Base_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleBase);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Child_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleChild);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleChild),
                    typeof(SampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Base_Interface_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(ISampleBase);

            _expectedTypes =
                new List<Type>
                {
                    typeof(ISampleBase)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Child_Interface_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(ISampleChild);

            _expectedTypes =
                new List<Type>
                {
                    typeof(ISampleBase),
                    typeof(ISampleChild)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Implementation_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleImplementation);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleImplementation),
                    typeof(SampleChild),
                    typeof(SampleBase),
                    typeof(ISampleChild),
                    typeof(ISampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Interface_Instance_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        class Foo : ISampleChild { }

        protected override void Given()
        {
            var foo = new Foo();
            _sut = foo.GetType();

            _expectedTypes =
                new List<Type>
                {
                    typeof(Foo),
                    typeof(ISampleChild),
                    typeof(ISampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    sealed class SampleStandalone { }
    abstract class SampleBase { }
    class SampleChild : SampleBase { }
    interface ISampleBase { }
    interface ISampleChild : ISampleBase { }
    class SampleImplementation : SampleChild, ISampleChild { }
}
diegosasw
quelle
0
   public IList<T> GetClassByType<T>()
   {
        return AppDomain.CurrentDomain.GetAssemblies()
                          .SelectMany(s => s.GetTypes())
                          .ToList(p => typeof(T)
                          .IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
                          .SelectList(c => (T)Activator.CreateInstance(c));
   }
Jonathan Santiago
quelle
0

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

private static IList<Type> loadAllImplementingTypes(Type[] interfaces)
{
    IList<Type> implementingTypes = 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)
                        implementingTypes.Add(currentType);
            }
            catch { }

    return implementingTypes;
}
akop
quelle
-3

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?

Ryan Rinaldi
quelle
6
Es könnte besser lesbar sein, wenn es funktioniert. Leider prüft Ihre where-Klausel, ob eine Instanz der System.Type-Klasse ISomeInterface implementiert, was niemals wahr sein wird, es sei denn, ISomeInterface ist wirklich IReflect oder ICustomAttributeProvider. In diesem Fall ist es immer wahr.
Joel Mueller
Die obige Antwort von Carl Nayak hat die Antwort auf die Korrektur der where-Klausel: IsAssignableFrom. Einfacher Fehler für eine Antwort.
TamusJRoyce