Obtenir tous les types qui implémentent une interface
-
09-06-2019 - |
Question
En utilisant la réflexion, comment puis-je obtenir tous les types qui implémentent une interface avec C# 3.0/.NET 3.5 avec le moins de code et en minimisant les itérations ?
C'est ce que je veux réécrire :
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
La solution
Le mien serait ceci en c# 3.0 :)
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
Fondamentalement, le moins d’itérations sera toujours :
loop assemblies
loop types
see if implemented.
Autres conseils
Cela a fonctionné pour moi.Il parcourt les classes et vérifie si elles sont dérivées de myInterface
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
Pour rechercher tous les types dans un assembly qui implémentent l’interface IFoo :
var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type;
Notez que la suggestion de Ryan Rinaldi était incorrecte.Il renverra 0 type.Tu ne peux pas écrire
where type is IFoo
car type est une instance System.Type et ne sera jamais de type IFoo.Au lieu de cela, vous vérifiez si IFoo est attribuable au type.Cela obtiendra les résultats attendus.
De plus, la suggestion d'Adam Wright, qui est actuellement marquée comme réponse, est également incorrecte, et pour la même raison.Au moment de l'exécution, vous verrez 0 type revenir, car toutes les instances System.Type n'étaient pas des implémenteurs IFoo.
J'apprécie que ce soit une très vieille question mais j'ai pensé ajouter une autre réponse pour les futurs utilisateurs car toutes les réponses à ce jour utilisent une certaine forme de Assembly.GetTypes
.
Bien que GetTypes() renvoie effectivement tous les types, cela ne signifie pas nécessairement que vous pouvez les activer et donc potentiellement lancer un ReflectionTypeLoadException
.
Un exemple classique de ne pas pouvoir activer un type serait lorsque le type renvoyé est derived
depuis base
mais base
est défini dans un assemblage différent de celui de derived
, un assembly auquel l’assembly appelant ne fait pas référence.
Disons donc que nous avons :
Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA
Si dans ClassC
lequel est dedans AssemblyC
nous faisons ensuite quelque chose selon la réponse acceptée :
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
Ensuite, il lancera un ReflectionTypeLoadException
.
C'est parce que sans référence à AssemblyA
dans AssemblyC
vous ne pourriez pas :
var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
Autrement dit ClassB
n'est pas chargeable c'est quelque chose que l'appel à GetTypes vérifie et lance.
Donc, pour qualifier en toute sécurité l'ensemble de résultats pour les types chargeables, selon ceci Phil Haacké article Obtenir tous les types dans un assemblage et Code de Jon Skeet vous feriez plutôt quelque chose comme :
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);
}
}
}
Et puis:
private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
var it = typeof (IMyInterface);
return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
D'autres réponses ici utilisent IsAssignableFrom
.Vous pouvez aussi utiliser FindInterfaces
du System
espace de noms, comme décrit ici.
Voici un exemple qui vérifie tous les assemblys dans le dossier de l'assembly en cours d'exécution, à la recherche de classes qui implémentent une certaine interface (en évitant LINQ pour plus de clarté).
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();
}
Vous pouvez configurer une liste d’interfaces si vous souhaitez en faire correspondre plusieurs.
parcourez tous les assemblys chargés, parcourez tous leurs types et vérifiez s’ils implémentent l’interface.
quelque chose comme:
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
}
}
}
Cela a fonctionné pour moi (si vous le souhaitez, vous pouvez exclure les types de systèmes dans la recherche) :
Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
t => lookupType.IsAssignableFrom(t) && !t.IsInterface);
Modifier:Je viens de voir la modification pour clarifier que la question initiale concernait la réduction des itérations/du code et c'est très bien comme exercice, mais dans des situations réelles, vous souhaiterez la mise en œuvre la plus rapide, quelle que soit la manière dont cool l'apparence LINQ sous-jacente.
Voici ma méthode Utils pour parcourir les types chargés.Il gère les classes normales ainsi que les interfaces, et l'option exclureSystemTypes accélère considérablement les choses si vous recherchez des implémentations dans votre propre base de code ou dans celle d'un tiers.
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;
}
Ce n'est pas joli, je l'admets.
L'autre réponse ne fonctionnait pas avec un interface générique.
Celui-ci le fait, remplacez simplement typeof(ISomeInterface) par 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();
Donc avec
AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
nous obtenons toutes les assemblées
!x.IsInterface && !x.IsAbstract
est utilisé pour exclure l'interface et les abstraits et
.Select(x => x.Name).ToList();
pour les avoir dans une liste.
Il n’existe pas de moyen simple (en termes de performances) de faire ce que vous voulez faire.
Reflection fonctionne principalement avec des assemblys et des types, vous devrez donc obtenir tous les types de l'assembly et les interroger pour connaître la bonne interface.Voici un exemple :
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
Cela vous donnera tous les types qui implémentent IMyInterface dans Assembly MyAssembly
Encore mieux lors du choix du lieu de montage.Filtrez la plupart des assemblys si vous savez que toutes vos interfaces implémentées se trouvent dans le même 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();
J'ai des exceptions dans le code Linq donc je le fais de cette façon (sans extension compliquée) :
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;
}
Vous pouvez utiliser LINQ pour obtenir la liste :
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
Mais vraiment, est-ce plus lisible ?