Ottenere tutti i tipi che implementano un'interfaccia


554

Usando reflection, come posso ottenere tutti i tipi che implementano un'interfaccia con C # 3.0 / .NET 3.5 con il minimo codice e minimizzare le iterazioni?

Questo è ciò che voglio riscrivere:

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

1
Il codice di esempio funziona? Ho falsi negativi con la tua condizione if.
Imperatore Orionii,

3
L'istruzione if nel codice sopra sarà sempre falsa perché stai verificando se un'istanza della classe Type (t) implementa la tua interfaccia che non lo farà a meno che Type erediti IMyInterface (nel qual caso sarà sempre vero).
Liazy,

Risposte:


808

Il mio sarebbe questo in c # 3.0 :)

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

Fondamentalmente, il minor numero di iterazioni sarà sempre:

loop assemblies  
 loop types  
  see if implemented.

194
Si noti che l'elenco può anche includere l'interfaccia stessa. Cambia l'ultima riga in .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);per filtrarla (o p.IsClass).
jtpereyda,

39
Nota: questa risposta è errata! In questo modo viene verificata la "Compatibilità delle assegnazioni" e non se l'interfaccia è implementata. Ad esempio List<string>, non implementa, IEnumerable<object>ma questo metodo tornerà vero in .Net 4.0 a causa della covarianza che è davvero sbagliata. La risposta corretta è qui
Sriram Sakthivel

20
@SriramSakthivel prima di tutto, non sono stati specificati valori generici. In secondo luogo, questa domanda precede la covarianza. Terzo, supponete che il ritorno covariante non sia qualcosa che vogliono.
Darren Kopp,

24
Hai assolutamente ragione, tesoro, so che questo è un vecchio thread, ho appena registrato il mio commento solo per far sì che i futuri utenti sappiano che esiste un tale problema. Per non offenderti. e come dice il titolo della domanda se OP sta chiedendo Ottenere tutti i tipi che implementano un'interfaccia, questo codice non lo sta facendo. ma quasi tutti i casi funziona , senza dubbio. ci sono anche casi angolari come ho detto. Solo per esserne consapevole;
Sriram Sakthivel,

9
Sarà inoltre necessario assicurarsi che la classe non sia astratta =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis il

66

Questo ha funzionato per me. Esegue il loop delle classi e verifica se sono derivati ​​da myInterface

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

5
Si presuppone che l'assembly sia nell'eseguibile principale. Non un progetto aggiuntivo. Stai anche ripetendo inutilmente una serie di iterazioni. È meglio che il framework esegua il sollevamento di carichi pesanti. Quindi filtrare più in basso una volta trovato. Se pertinente, aggiorna la tua risposta. Includi ragionamento Elenco <T>. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Where (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces (). Contiene ( )); foreach (var item in items) Console.Log (item.Name);
TamusJRoyce,

58

Per trovare tutti i tipi in un assembly che implementano l'interfaccia IFoo:

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

Si noti che il suggerimento di Ryan Rinaldi era errato. Restituirà 0 tipi. Non puoi scrivere

where type is IFoo

perché type è un'istanza System.Type e non sarà mai di tipo IFoo. Invece, controlli per vedere se IFoo è assegnabile dal tipo. Ciò otterrà i risultati previsti.

Inoltre, anche il suggerimento di Adam Wright, che è attualmente contrassegnato come risposta, è errato e per lo stesso motivo. In fase di esecuzione, verranno visualizzati 0 tipi, poiché tutte le istanze System.Type non erano implementatori IFoo.


58

Apprezzo che questa sia una domanda molto vecchia, ma ho pensato di aggiungere un'altra risposta per gli utenti futuri, dato che tutte le risposte fino ad oggi usano una qualche forma di Assembly.GetTypes.

Mentre GetTypes () restituirà effettivamente tutti i tipi, ciò non significa necessariamente che potresti attivarli e quindi potenzialmente lanciare un ReflectionTypeLoadException.

Un esempio classico per non poter attivare un tipo potrebbe essere quando il tipo restituito derivedproviene basema baseè definito in un assembly diverso da quello di derived, un assembly a cui l'assembly chiamante non fa riferimento.

Quindi diciamo che abbiamo:

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

Se in ClassCquale è dentro AssemblyC, facciamo qualcosa secondo la risposta accettata:

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

Quindi lancerà a ReflectionTypeLoadException.

Questo perché senza un riferimento a AssemblyA in AssemblyCte non sarebbe in grado di:

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

In altre parole, ClassBnon è caricabile, il che è qualcosa che la chiamata a GetTypes verifica e genera.

Quindi, per qualificare in modo sicuro il set di risultati per tipi caricabili, in base a questo articolo di Phil Haacked Ottieni tutti i tipi in un codice Assembly e Jon Skeet dovresti invece fare qualcosa del tipo:

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

E poi:

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

3
Questo mi ha aiutato ad affrontare un problema molto strano, in cui nel mio progetto di test GetTypes avrebbe fallito e solo nel nostro ambiente CI. GetLoadableTypes era una soluzione per questa soluzione. L'errore non sarebbe riproducibile nell'ambiente locale ed era questo: System.Reflection.ReflectionTypeLoadException: impossibile caricare uno o più dei tipi richiesti. Recupera la proprietà LoaderExceptions per ulteriori informazioni. Più specificamente, si lamentava che esisteva un tipo che non aveva un'implementazione concreta ed è successo nel progetto di unit test. Grazie per questo!
Lari Tuomisto,

2
Questa risposta dovrebbe essere contrassegnata come soluzione, mi ha salvato il culo oggi, perché come ha detto @Lari Tuomisto, su env locale non siamo riusciti a ri-produrre un errore simile
Lightning3

3
Nel caso in cui aiuti qualcun altro: questa soluzione ha funzionato per me, ma ho dovuto modificarla per rimuovere il tipo di interfaccia dall'elenco. Volevo attivare CreateInstanceper tutti loro, e un'eccezione è stata lanciata quando stava cercando di creare l'interfaccia reale (che mi ha confuso per un po 'quando pensavo che l'interfaccia reale fosse fuori mano in questa soluzione). Quindi ho cambiato il codice in GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña,

21

Altre risposte qui usano IsAssignableFrom. Puoi anche usare FindInterfacesdallo Systemspazio dei nomi, come descritto qui .

Ecco un esempio che controlla tutti gli assembly nella cartella dell'assembly in esecuzione, cercando le classi che implementano una determinata interfaccia (evitando LINQ per chiarezza).

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

È possibile impostare un elenco di interfacce se si desidera abbinare più di una.


Questo cerca il nome dell'interfaccia stringa che è quello che stavo cercando.
senthil,

Funziona quando si carica un assembly in un dominio diverso, poiché il tipo deve essere serializzato in una stringa. davvero splendido!
TamusJRoyce,

Ottengo: impossibile risolvere la dipendenza dall'assembly 'System.Core, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089' perché non è stato precaricato. Quando si utilizzano le API ReflectionOnly, gli assembly dipendenti devono essere precaricati o caricati su richiesta tramite l'evento ReflectionOnlyAssemblyResolve.
bkwdesign,

18

scorrere tutti gli assiemi caricati, scorrere tutti i loro tipi e verificare se implementano l'interfaccia.

qualcosa di simile a:

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

8

Questo ha funzionato per me (se lo desideri, puoi escludere i tipi di sistema nella ricerca):

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

5

Modifica: ho appena visto la modifica per chiarire che la domanda originale era per la riduzione di iterazioni / codice e che va bene come esercizio, ma in situazioni del mondo reale vorrai l'implementazione più veloce, indipendentemente da di quanto sia bello il LINQ sottostante.

Ecco il mio metodo Utils per scorrere i tipi caricati. Gestisce le classi regolari e le interfacce e l'opzione excludeSystemTypes accelera enormemente le cose se stai cercando implementazioni nella tua base di codice di terze parti.

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

Non è carino, lo ammetto.


2
Gli enumeratori implementano IDisposable che non viene eliminato in un tentativo / infine. È meglio usare un foreach o un linq.
TamusJRoyce,

Perché stai testando excludeSystemTypesdue volte in uno if?
NetMage l'

4

Altre risposte non funzionavano con un'interfaccia generica .

Questo lo fa, basta sostituire typeof (ISomeInterface) con 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();

Quindi con

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

riceviamo tutte le assemblee

!x.IsInterface && !x.IsAbstract

è usato per escludere l'interfaccia e quelli astratti e

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

per averli in un elenco.


Spiega come funziona la tua soluzione e perché è superiore a tutte le altre risposte.
Lukas Körfer,

Non è superiore o inferiore, le altre risposte non hanno funzionato per me e mi sono preso la briga di condividerlo.
Antonin GAVREL,

Il mio commento riguardava solo la risposta del tuo codice, quindi ti ho chiesto di aggiungere qualche spiegazione.
Lukas Körfer,

2

Non esiste un modo semplice (in termini di prestazioni) per fare ciò che vuoi fare.

Reflection funziona principalmente con assembly e tipi, quindi dovrai ottenere tutti i tipi di assembly e interrogarli per la giusta interfaccia. Ecco un esempio:

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

In questo modo otterrai tutti i tipi che implementano IMyInterface in Assembly MyAssembly


2

Ancora meglio quando si sceglie la posizione dell'Assemblea. Filtra la maggior parte degli assembly se sai che tutte le interfacce implementate si trovano nello stesso 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();

Di Can Bilgin



1

Esistono già molte risposte valide, ma vorrei aggiungere un'altra implementazione come estensione del tipo e un elenco di test unitari per dimostrare diversi scenari:

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

Questo algoritmo supporta i seguenti scenari:

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

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

0

Ho ottenuto delle eccezioni nel codice linq, quindi lo faccio in questo modo (senza un'estensione complicata):

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

-3

È possibile utilizzare alcuni LINQ per ottenere l'elenco:

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

Ma davvero, è più leggibile?


6
Potrebbe essere più leggibile, se funzionasse. Sfortunatamente, la tua clausola where sta verificando se un'istanza della classe System.Type implementa ISomeInterface, il che non sarà mai vero, a meno che ISomeInterface sia realmente IReflect o ICustomAttributeProvider, nel qual caso sarà sempre vero.
Joel Mueller,

La risposta di Carl Nayak sopra ha la risposta alla correzione della clausola where: IsAssignableFrom. Semplice errore per una risposta.
TamusJRoyce,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.