Инициализировать все классы, реализующие определенный интерфейс

17

У меня есть интерфейс IExample и набор классов ClassOne , ClassTwo и ClassThree , все они определены в разных пространствах имен. Я, возможно, удалю любой из классов или добавлю новый в новое место на более позднем этапе разработки.

Теперь я хочу найти все типы, которые реализуют IExample во время выполнения, и создавать их . (Я знаю заранее, что ни один класс, реализующий IExample , никогда не будет нуждаться в каких-либо аргументах конструктора, но я не знаю, как указать это в коде, так что это я, а не компилятор, - который знает ...)

Возможно ли это? Как мне это сделать?

Обновление: Теперь я попробовал несколько предложенных подходов, но на всех из них, строка Activator.CreateInstance(type) , я получаю исключение System.MissingMethodException, потому что я не могу создать экземпляр интерфейс." Это мой код:

var tasks = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(a => a.GetTypes())
    .Where(t => typeof(IBootstrapperTask).IsAssignableFrom(t))

    // This line is where it fails
    .Select(t => Activator.CreateInstance(t) as IBootstrapperTask)

    .ToArray();
new AutoMapperBootstrapper(tasks).Initialize();

Без предложения as я не вижу никакого исключения, но мне присваивается object[] , и мне нужен IBootstrapperTask[] для конструктора на последней строке в выдержке. Я пробовал различные способы его бросить, но никто не работает.

    
задан Tomas Lycken 25.02.2011 в 18:40
источник

4 ответа

28

Это можно сделать с помощью Reflection. Например

var interfaceType = typeof(IExample);
var all = AppDomain.CurrentDomain.GetAssemblies()
  .SelectMany(x => x.GetTypes())
  .Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
  .Select(x => Activator.CreateInstance(x));

Примечание. Это создаст экземпляры IExample из сборников, загруженных в текущую AppDomain . Это может отличаться от всех сборок, загруженных в текущий процесс. Однако для многих типов приложений это будет эквивалентно.     

ответ дан JaredPar 25.02.2011 в 18:47
источник
8

Вам нужно знать список сборок для просмотра, но тогда LINQ делает это относительно легко:

var instances = (from assembly in assemblies
                 from type in assembly
                 where !type.IsAbstract && 
                       type.IsClass &&
                       type.IsPublic &&
                       !type.IsGenericType &&
                       typeof(IExample).IsAssignableFrom(type)
                 let ctor = type.GetConstructor(Type.EmptyTypes)
                 where ctor != null && ctor.IsPublic
                 select (IExample) Activator.CreateInstance(type))
                .ToList();

Вы можете подумать о некоторых других ограничениях для добавления, но их довольно легко выразить:)

instances будет тогда List<IExample> .

EDIT: Я подозреваю, что мой код будет работать там, где ваш не сделал, потому что я специально исключаю неклассы. Я предполагаю, что ваш код пытается создать экземпляр самого интерфейса, т. Е. Когда t typeof(IBootstrapperTask) .

    
ответ дан Jon Skeet 25.02.2011 в 18:48
источник
5

Вам нужно, чтобы это происходило динамически? Есть ли когда-нибудь, когда у вас может быть тот, который вы не хотите создавать? Мне кажется, что это хороший пример для инъекций зависимостей (которые можно настроить). Например, Unity имеет метод ResolveAll .

Из приведенной ссылки:

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>();
    
ответ дан Christopherous 5000 25.02.2011 в 18:49
источник
0

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

        IEnumerable<Type> types = Assembly.GetExecutingAssembly()
            .GetTypes().Where(t=>t.GetInterfaces().Where(tt==typeof(IExample)).Count()>0);
        List<object> objects = new List<object>();
        foreach (Type type in types)
        {
            objects.Add(Activator.CreateInstance(type));
        }
    
ответ дан Aliostad 25.02.2011 в 18:44
источник