Получение всех типов, которые реализуют интерфейс


554

Используя отражение, как я могу получить все типы, которые реализуют интерфейс с C # 3.0 / .NET 3.5 с наименьшим количеством кода и минимизируя итерации?

Вот что я хочу переписать:

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

1
Работает ли пример кода? У меня есть ложные негативы с твоим условием if.
Император Орионий

3
Оператор if в приведенном выше коде всегда будет ложным, поскольку вы проверяете, реализует ли ваш экземпляр класса Type (t) интерфейс, чего не будет, если только Type не наследует IMyInterface (в этом случае он всегда будет истинным).
Лиази

Ответы:


808

Мой был бы это в c # 3.0 :)

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

По сути, наименьшее количество итераций всегда будет:

loop assemblies  
 loop types  
  see if implemented.

194
Обратите внимание, что список может также включать сам интерфейс. Измените последнюю строку на, .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);чтобы отфильтровать ее (или p.IsClass).
Jtpereyda

39
Примечание. Этот ответ неверен!, При этом проверяется «Совместимость назначений», а не реализуется ли интерфейс. Например List<string>, не реализует, IEnumerable<object>но этот метод вернет true в .Net 4.0 из-за ковариации, которая на самом деле неверна. Правильный ответ здесь
Шрирам Шактивель

20
Во-первых, @SriramSakthivel, общие значения не были указаны. Во-вторых, этот вопрос предшествует ковариации. В-третьих, вы делаете предположение, что ковариантный возврат - это не то, чего они хотят.
Даррен Копп

24
Вы абсолютно правы, дорогой, я знаю, что это старая ветка, я только что зарегистрировал свой комментарий только для того, чтобы будущие пользователи знали о существовании такой проблемы. Не обижать тебя. и как говорится в заголовке вопроса, если OP запрашивает получение всех типов, реализующих интерфейс, этот код этого не делает. но почти во всех случаях это работает , без сомнения. Есть и угловые случаи, как я уже сказал. Просто чтобы знать об этом;
Шрирам Шактивель

9
Также нужно будет убедиться, что класс не абстрактный =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis

66

Это сработало для меня. Он проходит по классам и проверяет, получены ли они из myInterface

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

5
Вы предполагаете, что сборка находится в главном исполняемом файле. Не дополнительный проект. Вы также выполняете итерации без необходимости, хотя и множество итераций. Лучше, чтобы каркас выполнял тяжелую работу. Затем отфильтруйте дальше, когда найдете. Если уместно, пожалуйста, обновите свой ответ. Включить список <T> рассуждений. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Где (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces (). содержит тип (my) )); foreach (var item in items) Console.Log (item.Name);
TamusJRoyce

58

Чтобы найти все типы в сборке, реализующей интерфейс IFoo:

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

Обратите внимание, что предложение Райана Ринальди было неверным. Он вернет 0 типов. Вы не можете написать

where type is IFoo

потому что тип является экземпляром System.Type и никогда не будет иметь тип IFoo. Вместо этого вы проверяете, присваивается ли IFoo тип. Это даст ожидаемые результаты.

Кроме того, предложение Адама Райта, которое в настоящее время помечено как ответ, также неверно и по той же причине. Во время выполнения вы увидите, что возвращаются 0 типов, потому что все экземпляры System.Type не были реализациями IFoo.


58

Я ценю, что это очень старый вопрос, но я подумал, что добавлю еще один ответ для будущих пользователей, так как все ответы на сегодняшний день используют ту или иную форму Assembly.GetTypes.

Хотя GetTypes () действительно возвращает все типы, это не обязательно означает, что вы можете активировать их и, таким образом, потенциально можете генерировать a ReflectionTypeLoadException.

Классическим примером того, что невозможно активировать тип, может быть случай, когда возвращаемый тип взят derivedиз, baseно baseопределен в сборке, отличной от derivedсборки, на которую не ссылается вызывающая сборка.

Так сказать, у нас есть:

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

Если в ClassCкотором AssemblyCмы находимся, то делаем что-то согласно принятому ответу:

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

Тогда это бросит ReflectionTypeLoadException.

Это происходит потому , что без ссылки AssemblyA на AssemblyCвас не будет в состоянии:

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

Другими словами ClassB, не загружается, что проверяет и генерирует вызов GetTypes.

Таким образом, чтобы безопасно квалифицировать набор результатов для загружаемых типов, в соответствии с этой статьей Фила Хаакеда « Получить все типы в сборке и код Jon Skeet» вместо этого вы должны сделать что-то вроде:

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

А потом:

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

3
Это помогло мне справиться с очень странной проблемой, когда в моем тестовом проекте GetTypes потерпел бы неудачу и только в нашей CI-среде. GetLoadableTypes был исправлением для этого решения. Ошибка не может быть воспроизведена в локальной среде, и это было следующим: System.Reflection.ReflectionTypeLoadException: Невозможно загрузить один или несколько запрошенных типов. Получите свойство LoaderExceptions для получения дополнительной информации. В частности, он жаловался на то, что существует тип, который не имеет конкретной реализации, и это произошло в проекте модульного тестирования. Спасибо за это!
Лари Туомисто

2
Этот ответ должен быть помечен как решение, сегодня он спас мою задницу, потому что, как сказал @Lari Tuomisto, в локальной среде env мы не можем воспроизвести подобную ошибку
Lightning3

3
В случае, если это поможет кому-то еще: это решение сработало для меня, но мне пришлось изменить его, чтобы удалить тип интерфейса из списка. Я хотел активировать их CreateInstanceдля всех, и было выдано исключение, когда он пытался создать реальный интерфейс (что на какое-то время меня смутило, когда я подумал, что в этом решении не работает настоящий интерфейс). Поэтому я изменил код на GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Ксавье Пенья,

21

Другие ответы здесь используют IsAssignableFrom. Вы также можете использовать FindInterfacesиз Systemпространства имен, как описано здесь .

Вот пример, который проверяет все сборки в папке исполняемой в данный момент сборки, ищет классы, которые реализуют определенный интерфейс (избегая LINQ для ясности).

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

Вы можете настроить список интерфейсов, если вы хотите соответствовать более чем одному.


Этот ищет имя интерфейса строки, которое я искал.
сентябрь

Работает при загрузке сборки в другом домене, так как тип должен быть сериализован в строку. очень здорово!
TamusJRoyce

Я получаю: не удается разрешить зависимость от сборки 'System.Core, версия = 4.0.0.0, культура = нейтральная, PublicKeyToken = b77a5c561934e089', поскольку она не была предварительно загружена. При использовании API ReflectionOnly зависимые сборки должны быть предварительно загружены или загружены по требованию через событие ReflectionOnlyAssemblyResolve.
bkwdesign

18

цикл по всем загруженным сборкам, цикл по всем их типам и проверка, реализуют ли они интерфейс.

что-то вроде:

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

Это сработало для меня (если вы хотите исключить системные типы из поиска):

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

5

Редактировать: я только что видел редактирование, чтобы уточнить, что исходный вопрос был для сокращения итераций / кода, и это все хорошо, как упражнение, но в реальных ситуациях вам понадобится самая быстрая реализация, независимо от того, о том, как здорово выглядит лежащий в основе LINQ.

Вот мой метод Utils для перебора загруженных типов. Он обрабатывает обычные классы, а также интерфейсы, а опция excludeSystemTypes значительно ускоряет работу, если вы ищете реализации в собственной / сторонней кодовой базе.

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

Это не красиво, я признаю.


2
Перечислители реализуют IDisposable, который не удаляется в попытке / наконец. Лучше использовать foreach или linq.
TamusJRoyce

Почему вы тестируете excludeSystemTypesдважды в одном if?
NetMage

4

Другой ответ не работал с универсальным интерфейсом .

Это делает, просто замените typeof (ISomeInterface) на 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();

Так с

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

мы получаем все сборки

!x.IsInterface && !x.IsAbstract

используется для исключения интерфейса и абстрактных и

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

иметь их в списке.


Пожалуйста, объясните, как работает ваше решение и почему оно превосходит все остальные ответы.
Лукас Кёрфер

Это не выше или ниже, другие ответы не работали для меня, и я удосужился поделиться им.
Антонин ГАВРЕЛЬ

Мой комментарий был о том, что ваш ответ только для кода, поэтому я попросил вас добавить некоторые пояснения.
Лукас Кёрфер,

2

Нет простого способа (с точки зрения производительности) сделать то, что вы хотите.

Reflection работает в основном со сборками и типами, поэтому вам нужно получить все типы сборок и запросить их для правильного интерфейса. Вот пример:

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

Это даст вам все типы, которые реализуют IMyInterface в сборке MyAssembly


2

Еще лучше при выборе места сборки. Отфильтруйте большинство сборок, если вы знаете, что все реализованные интерфейсы находятся в одном и том же 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();

Кан Билгин



1

Уже есть много правильных ответов, но я бы хотел добавить другую реализацию как расширение типа и список модульных тестов, чтобы продемонстрировать различные сценарии:

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

Этот алгоритм поддерживает следующие сценарии:

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

Я получил исключения в linq-коде, поэтому я делаю это так (без сложного расширения):

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

Вы можете использовать LINQ, чтобы получить список:

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

Но действительно ли это более читабельно?


6
Это могло бы быть более читабельным, если бы это работало. К сожалению, ваше предложение where проверяет, реализует ли экземпляр класса System.Type ISomeInterface, что никогда не будет истинным, если только ISomeInterface действительно не является IReflect или ICustomAttributeProvider, и в этом случае оно всегда будет истинным.
Джоэл Мюллер

Ответ Карла Найяка выше имеет ответ на исправление предложения where: IsAssignableFrom. Легкая ошибка для ответа.
TamusJRoyce
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.