Domanda

Utilizzo di riflessione, come è possibile ottenere tutti i tipi che implementano un'interfaccia con C# 3.0/.NET 3.5 con meno codice, e riducendo al minimo le iterazioni?

Questo è ciò che voglio ri-scrivere:

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

Soluzione

La mia 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.

Altri suggerimenti

Questo ha funzionato per me.Si loop anche se le classi e le verifiche per vedere se sono derrived da myInterface

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

Per trovare tutti i tipi, in un'assemblea che implementano IFoo interfaccia:

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

Nota che Ryan Rinaldi suggerimento non era corretto.Restituisce 0 tipi.Non si può scrivere

where type is IFoo

a causa del tipo di Sistema.Tipo di istanza, e non potrà mai essere di tipo IFoo.Invece, è possibile controllare per vedere se IFoo è assegnabile dal tipo.Che verrà ottenere i risultati attesi.

Inoltre, Adam Wright suggestione, che è attualmente contrassegnato come risposta, non è corretto, ed è per lo stesso motivo.In fase di runtime, vedrai 0 tipi di tornare indietro, perché tutto il Sistema.Istanze di tipo non erano IFoo implementors.

Apprezzo questa è una vecchia questione, ma ho pensato di aggiungere un'altra risposta per gli utenti futuri come tutte le risposte alla data di usare una qualche forma di Assembly.GetTypes.

Mentre GetTypes() ritornerà davvero di tutti i tipi, non necessariamente significa che si può attivare e potrebbe quindi potenzialmente gettare un ReflectionTypeLoadException.

Un esempio classico per non essere in grado di attivare un tipo è quando il tipo restituito è derived da base ma base è definito in un assembly diverso da quello di derived, l'assemblea che la convocazione dell'assemblea non fa riferimento.

Quindi, dire 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 ClassC che è in AssemblyC abbiamo poi fare qualcosa per accettato di rispondere:

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

Poi sarà lanciare un ReflectionTypeLoadException.

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

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

In altre parole ClassB non è caricabile che è qualcosa che la chiamata a GetTypes controlli e la getta via.

In modo sicuro qualificare il set di risultati per caricabili i tipi di cui al presente Phil Haacked articolo Ottenere Tutti i Tipi in un Assieme e Jon Skeet codice si dovrebbe invece fare qualcosa di simile:

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

Altre risposte qui IsAssignableFrom.È inoltre possibile utilizzare FindInterfaces dal System spazio dei nomi, come descritto qui.

Ecco un esempio che controlla tutti gli assembly assembly attualmente in esecuzione della cartella, cerca per le classi che implementano una certa 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 uno.

loop attraverso tutti gli assembly caricati, ciclo attraverso tutti i tipi, e controllare 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
        }
    }
}

Questo ha funzionato per me (se si desidera si può escludere tipi di sistema nella ricerca):

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

Edit:Ho appena visto il tuo edit per chiarire che la domanda originale era per la riduzione delle iterazioni / codice e questo è tutto bene e buono come un esercizio, ma in situazioni del mondo reale si sta andando a voler più rapida attuazione, indipendentemente da quanto freddo sottostante LINQ sembra.

Ecco il mio Utils metodo per scorrere i tipi caricati.Gestisce corsi regolari, così come le interfacce, e il excludeSystemTypes opzione velocità le cose enormemente se siete alla ricerca per le implementazioni in proprio / terzi codebase.

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 è piacevole, lo ammetto.

Un'altra risposta che non funziona con un interfaccia generica.

Di questo, basta sostituire typeof(ISomeInterface) da 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())

abbiamo tutte le assemblee

!x.IsInterface && !x.IsAbstract

viene utilizzato per escludere l'interfaccia e quelle astratte e

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

per avere un elenco.

Non esiste un modo semplice (in termini di prestazioni) per fare quello che si vuole fare.

Riflessione lavora con montaggi e tipi, principalmente, quindi dovrete ottenere tutti i tipi di montaggio e query di loro per il diritto di interfaccia.Ecco un esempio:

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

Che si ottiene tutti i tipi che implementano l'IMyInterface in Assemblea MyAssembly

Ancora meglio quando si sceglie la posizione di Montaggio.Filtrare la maggior parte delle assemblee se si conoscono tutti i tuoi implementato le interfacce sono all'interno della stessa Assemblea.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();

Da Bilgin

Ho avuto eccezioni in linq-codice in modo che io lo faccio in questo modo (senza una complicata estensione):

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

Si potrebbe usare un po ' di LINQ per ottenere l'elenco:

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

Ma in realtà, è più leggibile?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top