Отримання всіх типів, які реалізують інтерфейс


553

Використовуючи роздуми, як я можу отримати всі типи, які реалізують інтерфейс із 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 (у такому випадку це завжди буде правдою).
Лязі

Відповіді:


806

Моє б це в 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>але цей метод поверне істинну версію в .Net 4.0 через коваріацію, що насправді неправильно. Правильна відповідь тут
Шрірам Сактівель

20
Спочатку @SriramSakthivel, загальні значення не вказані. По-друге, це питання зумовлює коваріацію. По-третє, ви робите припущення, що коваріантне повернення - це не те, чого вони хочуть.
Даррен Копп

24
Ви абсолютно праві, Даррен, я знаю, що це стара тема, я просто зареєстрував свій коментар лише для майбутніх користувачів, щоб знати про існування такої проблеми. Щоб не образити вас. і як заголовок запитання говорить, якщо OP просить отримати всі типи, які реалізують інтерфейс, цей код не робить цього. але майже у всіх випадках це працює , без сумніву. Є і кутові випадки, як я вже сказав. Просто бути в курсі цього;
Шрірам Сактівель

9
Також потрібно переконатися, що клас не абстрактний =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis

66

Це працювало для мене. Він циклічно, хоча класи і перевіряє, чи вони походять з мого інтерфейсу

 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.GetInterfaceface (). )); foreach (var елемент у пунктах) 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 перевіряє та накидає.

Отже, щоб безпечно кваліфікувати набір результатів для типів, що завантажуються, відповідно до цієї статті Phil Haacked Отримати всі типи в зборах та код Джона Скета, ви замість цього зробите щось на кшталт:

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, на локальній обстановці ми не змогли повторити подібну помилку продукту
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();
}

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


Цей шукає ім'я строкового інтерфейсу, яке саме я шукав.
1313

Працює при завантаженні збірки в іншій області, оскільки тип повинен бути серіалізований у рядок. дуже приголомшливо!
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 для ітерації через завантажені типи. Він обробляє звичайні класи, а також інтерфейси, а опція isključiSystemTypes значно прискорює роботу, якщо ви шукаєте реалізації у власній / сторонній кодовій базі.

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

Немає простого способу (з точки зору продуктивності) робити те, що ви хочете зробити.

Відображення працює головним чином із збірками та типами, тому вам доведеться отримати всі типи складання та запитувати їх на правильний інтерфейс. Ось приклад:

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
Це може бути читабельніше, якби це спрацювало. На жаль, ваш пункт де перевіряє, чи примірник класу System.Type реалізує ISomeInterface, який ніколи не буде істинним, якщо ISomeInterface не є дійсно IReflect або ICustomAttributeProvider, і в цьому випадку це завжди буде правдою.
Джоель Мюллер

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