Question Obtenir tous les types qui implémentent une interface


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

446
2017-08-25 19:57


origine


Réponses:


Le mien serait ceci dans 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.

654
2017-08-25 20:11



Pour trouver 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 types. 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 assignable à partir du type. Cela vous donnera les résultats attendus.

En outre, la suggestion d'Adam Wright, qui est actuellement marquée comme la réponse, est également incorrecte, et pour la même raison. À l'exécution, vous verrez 0 types revenir, car toutes les instances de System.Type n'étaient pas des implémenteurs IFoo.


51
2017-08-25 20:20



Cela a fonctionné pour moi. Il boucle 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
 }

45
2017-09-26 12:55



J'apprécie que ce soit une très vieille question mais j'ai pensé que j'ajouterais une autre réponse pour les futurs utilisateurs car toutes les réponses à ce jour utilisent une certaine forme de Assembly.GetTypes.

Alors que GetTypes () retournera en effet tous les types, cela ne signifie pas nécessairement que vous pourriez les activer et ainsi potentiellement lancer un ReflectionTypeLoadException.

Un exemple classique pour ne pas pouvoir activer un type serait quand le type retourné est derived de base mais base est défini dans un assemblage différent de celui de derived, un assembly que l'assembly appelant ne référence pas.

Donc 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 dans ClassC lequel est dedans AssemblyC nous faisons alors 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 jettera 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);

En d'autres termes ClassB n'est pas chargeable ce qui est quelque chose que l'appel à GetTypes vérifie et lance.

Donc, pour qualifier en toute sécurité le jeu de résultats pour les types chargeables, puis selon ce Phil Haacked article Obtenir tous les types dans un assemblage et Code 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 alors:

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

42
2018-03-31 22:41



D'autres réponses utilisent ici IsAssignableFrom. Vous pouvez aussi utiliser FindInterfaces du System namespace, tel que 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 interface donnée (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 voulez en faire correspondre plus d'un.


18
2018-06-08 11:18



parcourir tous les assemblys chargés, parcourir tous leurs types et vérifier 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
        }
    }
}

13
2017-08-25 20:05



Cela a fonctionné pour moi (si vous souhaitez 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); 

7
2017-11-12 14:30



Edit: Je viens de voir l'édition pour clarifier que la question originale était pour la réduction des itérations / code et que tout cela est très bien comme un exercice, mais dans des situations réelles, vous allez vouloir la mise en œuvre la plus rapide, indépendamment de la fraîcheur du LINQ sous-jacent.

Voici ma méthode Utils pour parcourir les types chargés. Il gère les classes régulières ainsi que les interfaces, et l'option excludeSystemTypes accélère énormément les choses si vous recherchez des implémentations dans votre base de code propre / tierce.

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.


5
2017-08-25 20:12