Uzyskiwanie wszystkich typów, które implementują interfejs


553

Używając refleksji, w jaki sposób mogę uzyskać wszystkie typy, które implementują interfejs z C # 3.0 / .NET 3.5 z najmniejszym kodem i minimalizując iteracje?

Oto, co chcę ponownie napisać:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff

1
Czy przykładowy kod działa? Mam fałszywe negatywy z twoim warunkiem if.
Cesarz Orionii,

3
Instrukcja if w powyższym kodzie będzie zawsze fałszywa, ponieważ testujesz, czy instancja klasy Type (t) implementuje interfejs, czego nie zrobi, chyba że Type odziedziczy IMyInterface (w którym to przypadku zawsze będzie to prawda).
Liazy

Odpowiedzi:


806

Mój byłby to w c # 3.0 :)

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

Zasadniczo najmniejsza liczba iteracji zawsze będzie:

loop assemblies  
 loop types  
  see if implemented.

194
Pamiętaj, że lista może również zawierać sam interfejs. Zmień ostatni wiersz, aby .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);go odfiltrować (lub p.IsClass).
jtpereyda

39
Uwaga: ta odpowiedź jest nieprawidłowa !, sprawdza to „Kompatybilność przypisań”, a nie to, czy interfejs jest zaimplementowany. Na przykład List<string>nie implementuje, IEnumerable<object>ale ta metoda zwróci true w .Net 4.0 z powodu kowariancji, która jest rzeczywiście błędna. Prawidłowa odpowiedź jest tutaj
Sriram Sakthivel

20
@ SirramSakthivel po pierwsze, wartości ogólne nie zostały określone. Po drugie, pytanie to poprzedza kowariancję. Po trzecie, zakładasz, że powrót kowariantny nie jest czymś, czego chcą.
Darren Kopp

24
Masz absolutną rację kochanie, wiem, że to stary wątek, właśnie zarejestrowałem swój komentarz, aby przyszli użytkownicy mogli zdawać sobie sprawę z istnienia takiego problemu. Nie obrażać cię. i jak tytuł pytania mówi, jeśli OP prosi o uzyskanie wszystkich typów, które implementują interfejs, ten kod tego nie robi. ale prawie wszystkie przypadki działają bez wątpienia. są też przypadki narożne, jak powiedziałem. Po prostu być tego świadomym;
Sriram Sakthivel

9
Będę również musiał upewnić się, że klasa nie jest abstrakcyjna =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis,

66

To zadziałało dla mnie. Pętla przechodzi przez klasy i sprawdza, czy są one pobierane z mojego interfejsu

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }

5
Zakładasz, że zestaw znajduje się w głównym pliku wykonywalnym. Nie jest to dodatkowy projekt. Powtarzacie także niepotrzebnie kilka przeróbek. Lepiej jest, aby rama wykonała ciężkie podnoszenie. Następnie odfiltruj dalej, gdy zostanie znaleziony. W razie potrzeby zaktualizuj swoją odpowiedź. Uwzględnij argumentację Lista <T>. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Where (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces (typ). )); foreach (var item in items) Console.Log (item.Name);
TamusJRoyce,

58

Aby znaleźć wszystkie typy w zestawie, które implementują interfejs IFoo:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;

Zauważ, że sugestia Ryana Rinaldiego była nieprawidłowa. Zwróci 0 typów. Nie możesz pisać

where type is IFoo

ponieważ typ jest instancją System.Type i nigdy nie będzie typu IFoo. Zamiast tego sprawdzasz, czy IFoo można przypisać z tego typu. To zapewni oczekiwane rezultaty.

Również sugestia Adama Wrighta, która jest obecnie oznaczona jako odpowiedź, jest również niepoprawna i z tego samego powodu. W czasie wykonywania powróci 0 typów, ponieważ wszystkie instancje System.Type nie były implementatorami IFoo.


58

Rozumiem, że to bardzo stare pytanie, ale pomyślałem, że dodam kolejną odpowiedź dla przyszłych użytkowników, ponieważ wszystkie dotychczasowe odpowiedzi wykorzystują jakąś formę Assembly.GetTypes .

Chociaż GetTypes () rzeczywiście zwróci wszystkie typy, nie musi to oznaczać, że możesz je aktywować, a tym samym potencjalnie wyrzucić ReflectionTypeLoadException.

Klasycznym przykładem braku możliwości aktywacji typu jest sytuacja, gdy zwracany typ pochodzi derivedz zestawu, baseale basejest zdefiniowany w innym zestawie niż zestaw derived, do którego nie odwołuje się zestaw wywołujący.

Powiedzmy, że mamy:

Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA

Jeśli w ClassCktórym jest AssemblyC, to robimy coś zgodnie z przyjętą odpowiedzią:

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

Wtedy wyrzuci ReflectionTypeLoadException.

To dlatego, że bez odniesienia do AssemblyA w AssemblyCnie byłby w stanie:

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

Innymi słowy ClassBnie można go załadować co jest sprawdzane i uruchamiane przez wywołanie GetTypes.

Tak aby bezpiecznie zakwalifikować zestaw wyników dla ładowalnych typów wtedy jak na tym Phil Haacked artykule Get Wszystkie rodzaje w zespole i kod Jon Skeet byś zamiast zrobić coś takiego:

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

I wtedy:

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

3
Pomogło mi to poradzić sobie z bardzo dziwnym problemem, w którym w moim projekcie testowym GetTypes zawiódłoby i tylko w naszym środowisku CI. GetLoadableTypes był rozwiązaniem tego rozwiązania. Błąd nie byłby powtarzalny w środowisku lokalnym i był taki: System.Reflection.ReflectionTypeLoadException: Nie można załadować jednego lub więcej żądanych typów. Pobierz właściwość LoaderExceptions, aby uzyskać więcej informacji. Mówiąc dokładniej, narzekał, że istnieje typ, który nie ma konkretnej implementacji i zdarzyło się to w projekcie testu jednostkowego. Dzięki za to!
Lari Tuomisto,

2
Ta odpowiedź powinna być oznaczona jako rozwiązanie, uratowała mi dziś tyłek, ponieważ jak powiedział @Lari Tuomisto, na lokalnej env nie mogliśmy ponownie odtworzyć podobnego błędu
Lightning3

3
W przypadku, gdy pomaga komuś innemu: to rozwiązanie działało dla mnie, ale musiałem je zmodyfikować, aby usunąć typ interfejsu z listy. Chciałem aktywować CreateInstancedla wszystkich, a wyjątek został zgłoszony, gdy próbowałem stworzyć rzeczywisty interfejs (co sprawiło, że pomyliłem się przez chwilę, gdy myślałem, że rzeczywisty interfejs nie działa w tym rozwiązaniu). Więc zmieniłem kod na GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña,

21

Inne odpowiedzi tutaj IsAssignableFrom. Możesz także użyć FindInterfacesz Systemprzestrzeni nazw, jak opisano tutaj .

Oto przykład, który sprawdza wszystkie zestawy w aktualnie wykonywanym folderze zestawu, szukając klas, które implementują określony interfejs (unikając LINQ dla przejrzystości).

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

Możesz skonfigurować listę interfejsów, jeśli chcesz dopasować więcej niż jeden.


Ten szuka nazwy interfejsu łańcuchowego, której szukałem.
sentil

Działa podczas ładowania zestawu w innej domenie, ponieważ typ musi być serializowany do ciągu. bardzo niesamowite!
TamusJRoyce,

Otrzymuję: Nie można rozwiązać zależności do zestawu „System.Core, wersja = 4.0.0.0, Kultura = neutralny, PublicKeyToken = b77a5c561934e089”, ponieważ nie został on wstępnie załadowany. Podczas korzystania z interfejsów API ReflectionOnly zestawy zależne muszą być wstępnie ładowane lub ładowane na żądanie za pośrednictwem zdarzenia ReflectionOnlyAssemblyResolve.
bkwdesign

18

przeglądaj wszystkie załadowane zestawy, przeglądaj wszystkie ich typy i sprawdzaj, czy implementują interfejs.

coś jak:

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

8

To działało dla mnie (jeśli chcesz, możesz wykluczyć typy systemów podczas wyszukiwania):

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

5

Edycja: Właśnie widziałem edycję, aby wyjaśnić, że pierwotne pytanie dotyczyło ograniczenia iteracji / kodu i to wszystko dobrze i dobrze jako ćwiczenie, ale w rzeczywistych sytuacjach będziesz chciał najszybszej implementacji, niezależnie od tego jak fajnie wygląda bazowy LINQ.

Oto moja metoda Utils do iteracji wczytywanych typów. Obsługuje zarówno zwykłe klasy, jak i interfejsy, a opcja excludeSystemTypes znacznie przyspiesza działanie, jeśli szukasz implementacji we własnej bazie kodowej.

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;
}

Przyznaję, że to nie jest ładne.


2
Moduł wyliczający implementuje IDisposable, który nie jest usuwany podczas try / wreszcie. Lepiej jest użyć foreach lub linq.
TamusJRoyce

Dlaczego testujesz excludeSystemTypesdwa razy w jednym if?
NetMage,

4

Inne odpowiedzi nie działały z ogólnym interfejsem .

W tym przypadku wystarczy zastąpić typeof (ISomeInterface) typof (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();

Więc z

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

otrzymujemy wszystkie zespoły

!x.IsInterface && !x.IsAbstract

służy do wykluczenia interfejsu i abstrakcyjnych oraz

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

mieć je na liście.


Wyjaśnij, jak działa Twoje rozwiązanie i dlaczego jest ono lepsze od wszystkich innych odpowiedzi.
Lukas Körfer,

Nie jest lepszy ani niższy, inne odpowiedzi nie działały dla mnie i starałem się je udostępnić.
Antonin GAVREL,

Mój komentarz dotyczył tylko odpowiedzi na kod, więc poprosiłem o dodanie wyjaśnienia.
Lukas Körfer,

2

Nie ma łatwego (pod względem wydajności) sposobu robienia tego, co chcesz.

Refleksja działa głównie z zespołami i typami, więc będziesz musiał uzyskać wszystkie typy zespołu i zapytać o odpowiedni interfejs. Oto przykład:

Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);

Otrzymasz wszystkie typy, które implementują interfejs IMyInterface w MyAssembly zespołu


2

Jeszcze lepiej przy wyborze miejsca montażu. Filtruj większość zestawów, jeśli wiesz, że wszystkie zaimplementowane interfejsy znajdują się w tym samym 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();

Przez Can Bilgin



1

Istnieje już wiele poprawnych odpowiedzi, ale chciałbym dodać inną implementację jako rozszerzenie typu i listę testów jednostkowych w celu zademonstrowania różnych scenariuszy:

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;
            }
        }
    }
}

Ten algorytm obsługuje następujące scenariusze:

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 { }
}

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

0

Mam wyjątki w kodzie linq, więc robię to w ten sposób (bez skomplikowanego rozszerzenia):

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;
}

-3

Możesz użyć LINQ, aby uzyskać listę:

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

Ale czy naprawdę jest to bardziej czytelne?


6
Może być bardziej czytelny, jeśli zadziała. Niestety twoja klauzula where sprawdza, czy instancja klasy System.Type implementuje ISomeInterface, co nigdy nie będzie prawdziwe, chyba że ISomeInterface jest naprawdę IReflect lub ICustomAttributeProvider, w którym to przypadku zawsze będzie to prawdą.
Joel Mueller

Odpowiedź Carla Nayaka powyżej zawiera odpowiedź na poprawienie klauzuli where: IsAssignableFrom. Łatwy błąd dla odpowiedzi.
TamusJRoyce,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.