Obtenir tous les types qui implémentent une interface

554

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?

Voici ce que je veux réécrire:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
juan
la source
1
L'exemple de code fonctionne-t-il? J'ai de faux négatifs avec votre condition if.
Emperor Orionii
3
L'instruction if dans le code ci-dessus sera toujours fausse car vous testez si une instance de la classe Type (t) implémente votre interface, ce qu'elle ne fera pas, sauf si Type hérite d'IMyInterface (auquel cas elle sera toujours vraie).
Liazy

Réponses:

808

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.
Darren Kopp
la source
194
Notez que la liste peut également inclure l'interface elle-même. Remplacez la dernière ligne par .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);pour la filtrer (ou p.IsClass).
jtpereyda
39
Remarque: cette réponse est erronée !, cela vérifie que la "compatibilité des affectations" ne vérifie pas si l'interface est implémentée. Par exemple, List<string>ne met pas en œuvre, IEnumerable<object>mais cette méthode retournera vrai dans .Net 4.0 en raison de la covariance qui est en effet incorrecte. La bonne réponse est ici
Sriram Sakthivel
20
@SriramSakthivel tout d'abord, les valeurs génériques n'étaient pas spécifiées. Deuxièmement, cette question est antérieure à la covariance. Troisièmement, vous faites l'hypothèse que le rendement covariant n'est pas quelque chose qu'ils veulent.
Darren Kopp
24
Vous avez absolument raison Darren, je sais que c'est un vieux fil de discussion, je viens d'enregistrer mon commentaire juste pour que les futurs utilisateurs prennent conscience d'un tel problème. Ne pas vous offenser. et comme le titre de la question l'indique, si OP demande d' obtenir tous les types qui implémentent une interface, ce code ne le fait pas. mais presque tous les cas cela fonctionne , sans aucun doute. il y a aussi des cas d'angle comme je l'ai dit. Juste pour en être conscient;
Sriram Sakthivel
9
Il faudra également s'assurer que la classe n'est pas abstraite =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis
66

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
 }
Ben Watkins
la source
5
Vous supposez que l'assembly se trouve dans l'exécutable principal. Pas un projet supplémentaire. Vous répétez également inutilement un tas d'itérations. Il vaut mieux que le cadre fasse le gros du travail. Filtrer ensuite plus loin une fois trouvé. Le cas échéant, veuillez mettre à jour votre réponse. Inclure le raisonnement de la liste <T>. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Where (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces ()) Contient )); foreach (var item in items) Console.Log (item.Name);
TamusJRoyce
58

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 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 attribuable à partir du 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 types revenir, car toutes les instances System.Type n'étaient pas des implémenteurs IFoo.

Judah Gabriel Himango
la source
58

J'apprécie que c'est une très vieille question, mais j'ai pensé ajouter une autre réponse aux futurs utilisateurs car toutes les réponses à ce jour utilisent une forme de Assembly.GetTypes .

Bien que GetTypes () renvoie en effet tous les types, cela ne signifie pas nécessairement que vous pouvez les activer et donc potentiellement lancer un ReflectionTypeLoadException.

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

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 dans ClassClequel se trouve AssemblyCnous 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 lancera un ReflectionTypeLoadException.

En effet , sans référence à AssemblyA en AssemblyCvous ne seriez pas en mesure de:

var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);

En d'autres termes ClassBn'est pas chargeable ce qui 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, conformément à cet article de Phil Haacked Obtenir tous les types dans un code Assembly et Jon Skeet, vous devriez plutôt 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 alors:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
rism
la source
3
Cela m'a aidé à faire face à un problème super bizarre, où dans mon projet de test, GetTypes échouerait et uniquement dans notre environnement CI. GetLoadableTypes était un correctif pour cette solution. L'erreur ne serait pas reproductible dans l'environnement local et c'était la suivante: System.Reflection.ReflectionTypeLoadException: impossible de charger un ou plusieurs des types demandés. Récupérez la propriété LoaderExceptions pour plus d'informations. Plus précisément, il se plaignait d'un type qui n'avait pas de mise en œuvre concrète et cela s'est produit dans le projet de test unitaire. Merci pour cela!
Lari Tuomisto
2
Cette réponse devrait être marquée comme solution, elle m'a sauvé le cul aujourd'hui, car comme @Lari Tuomisto l'a dit, sur env local, nous ne pouvions pas reproduire une erreur similaire
Lightning3
3
Au cas où cela aiderait quelqu'un d'autre: cette solution a fonctionné pour moi, mais j'ai dû la modifier pour supprimer le type d'interface de la liste. Je voulais activer CreateInstancepour chacun d'entre eux, et une exception a été levée quand il essayait de créer l'interface réelle (ce qui m'a confus pendant un certain temps quand je pensais que l'interface réelle était hors de propos dans cette solution). J'ai donc changé le code en GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña
21

D'autres réponses ici utilisent IsAssignableFrom. Vous pouvez également utiliser à FindInterfacespartir de l' Systemespace 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.

hillstuk
la source
Celui-ci recherche le nom d'interface de chaîne, ce que je cherchais.
senthil
Fonctionne lors du chargement d'un assemblage dans un domaine différent, car le type doit être sérialisé en chaîne. très impressionnant!
TamusJRoyce
J'obtiens: Impossible de résoudre la dépendance à l'assembly 'System.Core, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089' car il n'a pas été préchargé. Lorsque vous utilisez les API ReflectionOnly, les assemblys dépendants doivent être préchargés ou chargés à la demande via l'événement ReflectionOnlyAssemblyResolve.
bkwdesign
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
        }
    }
}
Lasse V. Karlsen
la source
8

Cela a fonctionné pour moi (si vous le souhaitez, vous pouvez exclure les types de système dans la recherche):

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 
Carl Nayak
la source
5

Edit: je viens de voir l'édition pour clarifier que la question d'origine était pour la réduction des itérations / du code et c'est bien beau comme exercice, mais dans des situations du monde réel, vous voudrez 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 propre base de code / 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, j'avoue.

tags2k
la source
2
Les énumérateurs implémentent IDisposable qui n'est pas éliminé lors d'un essai / enfin. Il est préférable d'utiliser un foreach ou un linq.
TamusJRoyce
Pourquoi testez-vous excludeSystemTypesdeux fois en un if?
NetMage
4

L'autre réponse ne fonctionnait pas avec une interface générique .

Celui-ci le fait, 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();

Donc avec

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

nous obtenons tous les assemblages

!x.IsInterface && !x.IsAbstract

est utilisé pour exclure l'interface et les abstraites et

.Select(x => x.Name).ToList();

pour les avoir dans une liste.

Antonin GAVREL
la source
Veuillez expliquer comment fonctionne votre solution et pourquoi elle est supérieure à toutes les autres réponses.
Lukas Körfer
Ce n'est ni supérieur ni inférieur, les autres réponses n'ont pas fonctionné pour moi et j'ai pris la peine de le partager.
Antonin GAVREL
Mon commentaire portait sur le fait que votre réponse était uniquement codée, je vous ai donc demandé d'ajouter quelques explications.
Lukas Körfer
2

Il n'y a pas de moyen facile (en termes de performances) de faire ce que vous voulez faire.

La réflexion fonctionne principalement avec les assemblys et les types, vous devrez donc obtenir tous les types d'assembly et les interroger pour obtenir 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 l'assembly MyAssembly

Jorge Córdoba
la source
2

Encore mieux lors du choix du lieu de montage. Filtrez la plupart des assemblys si vous savez que toutes vos interfaces implémentées sont dans les mêmes 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

user489566
la source
1

Il existe déjà de nombreuses réponses valides, mais j'aimerais ajouter une implémentation d'anthère en tant qu'extension de type et une liste de tests unitaires pour illustrer différents scénarios:

public static class TypeExtensions
{
    public static IEnumerable<Type> GetAllTypes(this Type type)
    {
        var typeInfo = type.GetTypeInfo();
        var allTypes = GetAllImplementedTypes(type).Concat(typeInfo.ImplementedInterfaces);
        return allTypes;
    }

    private static IEnumerable<Type> GetAllImplementedTypes(Type type)
    {
        yield return type;
        var typeInfo = type.GetTypeInfo();
        var baseType = typeInfo.BaseType;
        if (baseType != null)
        {
            foreach (var foundType in GetAllImplementedTypes(baseType))
            {
                yield return foundType;
            }
        }
    }
}

Cet algorithme prend en charge les scénarios suivants:

public static class GetAllTypesTests
{
    public class Given_A_Sample_Standalone_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleStandalone);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleStandalone),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Abstract_Base_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleBase);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Child_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleChild);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleChild),
                    typeof(SampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Base_Interface_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(ISampleBase);

            _expectedTypes =
                new List<Type>
                {
                    typeof(ISampleBase)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Child_Interface_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(ISampleChild);

            _expectedTypes =
                new List<Type>
                {
                    typeof(ISampleBase),
                    typeof(ISampleChild)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Implementation_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleImplementation);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleImplementation),
                    typeof(SampleChild),
                    typeof(SampleBase),
                    typeof(ISampleChild),
                    typeof(ISampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Interface_Instance_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        class Foo : ISampleChild { }

        protected override void Given()
        {
            var foo = new Foo();
            _sut = foo.GetType();

            _expectedTypes =
                new List<Type>
                {
                    typeof(Foo),
                    typeof(ISampleChild),
                    typeof(ISampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    sealed class SampleStandalone { }
    abstract class SampleBase { }
    class SampleChild : SampleBase { }
    interface ISampleBase { }
    interface ISampleChild : ISampleBase { }
    class SampleImplementation : SampleChild, ISampleChild { }
}
diegosasw
la source
0
   public IList<T> GetClassByType<T>()
   {
        return AppDomain.CurrentDomain.GetAssemblies()
                          .SelectMany(s => s.GetTypes())
                          .ToList(p => typeof(T)
                          .IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
                          .SelectList(c => (T)Activator.CreateInstance(c));
   }
Jonathan Santiago
la source
0

J'ai des exceptions dans le code linq donc je le fais de cette façon (sans extension compliquée):

private static IList<Type> loadAllImplementingTypes(Type[] interfaces)
{
    IList<Type> implementingTypes = 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)
                        implementingTypes.Add(currentType);
            }
            catch { }

    return implementingTypes;
}
akop
la source
-3

Vous pouvez utiliser du LINQ pour obtenir la liste:

var types = from type in this.GetType().Assembly.GetTypes()
            where type is ISomeInterface
            select type;

Mais est-ce vraiment plus lisible?

Ryan Rinaldi
la source
6
Cela pourrait être plus lisible, si cela fonctionnait. Malheureusement, votre clause where vérifie si une instance de la classe System.Type implémente ISomeInterface, ce qui ne sera jamais vrai, sauf si ISomeInterface est vraiment IReflect ou ICustomAttributeProvider, auquel cas ce sera toujours vrai.
Joel Mueller
La réponse de Carl Nayak ci-dessus permet de corriger la clause where: IsAssignableFrom. Erreur facile pour une réponse.
TamusJRoyce