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
Était-ce utile?

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

Par Can Bilgin

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 ?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top