Ottenere tutti i tipi che implementano un'interfaccia
-
09-06-2019 - |
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
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();
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?