Obtenir tous les types qui mettent en œuvre une interface
en utilisant la réflexion, comment puis-je obtenir tous les types qui mettent en œuvre 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
12 réponses
Mine 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.
pour trouver tous les types dans un assemblage qui implémente 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 retournera 0 types. Vous ne pouvez pas écrire
where type is IFoo
parce que le type est un système.Type instance, et ne sera jamais de type IFoo. Au lieu de cela, vous vérifiez si IFoo est assignable à partir du type. Vous obtiendrez les résultats escomptés.
aussi, la suggestion D'Adam Wright, qui est actuellement marqué comme réponse, la réponse est incorrecte, et pour la même raison. À l'exécution, vous verrez 0 types revenir, parce que tout le système.Les instances de Type N'étaient pas IFoo implementors.
ça a marché pour moi. Il boucle à travers les classes et vérifie pour voir 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
}
je comprends que c'est une question très ancienne, mais j'ai pensé que je voudrais ajouter une autre réponse pour les futurs utilisateurs que toutes les réponses à ce jour utilisent une certaine forme de Assembly.GetTypes
.
alors que GetTypes () retournera effectivement tous les types, cela ne signifie pas nécessairement que vous pourriez les activer et donc potentiellement jeter un ReflectionTypeLoadException
.
Un exemple classique pour ne pas être en mesure d'activer un type de serait lorsque le type retourné est derived
de base
mais base
est défini dans un assemblage différent de celui de derived
, un assemblage auquel l'ensemble appelant ne fait pas référence.
Alors disons 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 ClassC
qui est dans AssemblyC
nous de faire quelque chose comme par accepté de répondre:
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
alors il lancera un ReflectionTypeLoadException
.
c'est parce que sans une référence à AssemblyA
dans AssemblyC
vous ne seriez pas en mesure de:
var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
en d'autres termes ClassB
n'est pas chargeable ce qui est quelque chose que l'appel à GetTypes vérifie et lance.
ainsi pour qualifier en toute sécurité l'ensemble de résultats pour les types chargeables puis selon ce Phil Haacked article obtenir tous les Types dans un Assemblée et Jon Skeet code vous serait plutôt de faire 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();
}
autres réponses utilisez ici IsAssignableFrom
. Vous pouvez également utiliser FindInterfaces
de l'espace de noms System
, comme décrit ici .
voici un exemple qui vérifie tous les assemblages dans le dossier de l'assemblage 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 définir une liste d'interfaces si vous voulez correspondre à plus d'un.
boucle à travers tous les assemblages chargés, boucle à travers tous leurs types, et de vérifier s'ils mettent en œuvre 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);
Edit: je viens de voir l'edit pour clarifier que la question originale était pour la réduction des itérations / code et c'est tout bien et bon comme un exercice, mais dans des situations réelles, vous allez vouloir l'implémentation la plus rapide, indépendamment de la façon dont cool le LINQ sous-jacent semble.
voici ma méthode Utils pour itérer à travers les types chargés. Il gère les classes régulières ainsi que les interfaces, et l'option exclusidesystemtypes accélère considérablement les choses si vous êtes à la recherche d'implémentations dans votre propre / base de codes 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.
il n'y a pas de moyen facile (en termes de performance) de faire ce que vous voulez faire.
réflexion fonctionne avec des assemblages et des types principalement de sorte que vous aurez à obtenir tous les types de l'assemblage et de les interroger pour la bonne interface. Voici un exemple:
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
qui vous fournira tous les types qui mettent en œuvre L'IMyInterface dans L'assemblage MyAssembly
j'ai eu 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;
}
autre réponse ne fonctionnait pas avec une interface générique .
celui-ci ne, il suffit de remplacer 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();
ainsi que
AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
nous obtenons tous les assemblages
!x.IsInterface && !x.IsAbstract
est utilisé pour exclure l'interface et abstraits et
.Select(x => x.Name).ToList();
de les avoir dans une liste.
, Vous pouvez utiliser certains de LINQ pour obtenir la liste:
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
mais est-ce plus lisible?