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

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
  • Однако, что еще более важно, оно будет включать только сборки, которые уже были загружены. Могут быть и другие, на которые ссылались, но еще не загружены. Лично я предпочитаю держать все явным :) –  Jon Skeet 25.02.2011 в 19:02
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
  • Спасибо за ваш ответ - см. мое обновление для получения дополнительной информации. –  Tomas Lycken 25.02.2011 в 19:24
  • @ Томас: Отредактировано. Вы попробовали мое решение? (Если вы хотите, измените последнюю строку на ToArray.) По общему признанию, я не могу понять, почему так было бы иметь значение для вашего исходного кода ... –  Jon Skeet 25.02.2011 в 19:28
  • Спасибо. Когда я посмотрел ближе к вашему коду, я заметил явные требования быть публичными, не абстрактными и с конструктором, а также реализовал их в моем коде. Теперь эта конкретная часть работает, но у меня есть другие (возможно, не связанные) проблемы, поэтому я не могу понять, действительно ли это работает, или просто выбрасывает другое исключение. –  Tomas Lycken 26.02.2011 в 00:13
5

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

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

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>();
    
ответ дан Christopherous 5000 25.02.2011 в 18:49
  • Только с ограниченной информацией о примере, которую я дал в моем вопросе, это допустимый аргумент, но в этом случае DI не применим. –  Tomas Lycken 25.02.2011 в 19:25
  • Ха - только потому, что я так быстро уволил ДИ, выясняется, что я все равно в конце концов ...: P Спасибо! Тем не менее, я не буду отмечать это как ответ, просто потому, что он не отвечает на вопрос («как мне найти все классы, которые реализуют этот интерфейс динамически?»), Хотя он решает мою фактическую проблему. –  Tomas Lycken 27.02.2011 в 23:41
  • +1 Большое спасибо! Я разрабатывал проект с использованием Caliburn.Micro для обработки всех моих связанных с MVVM вещей, и мне никогда не приходило в голову, что я могу использовать его для решения этой проблемы! Любой, кто любит это решение, должен попробовать эту инфраструктуру. –  CuddleBunny 01.08.2012 в 06:37
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
  • Почему бы вам просто не позвонить Содержит? Кроме того, это не будет обрабатывать наследование. –  SLaks 25.02.2011 в 19:03
  • Это не сработает, поскольку я не могу создавать экземпляры интерфейсов - я должен создавать экземпляры типов, которые их реализуют. –  Tomas Lycken 25.02.2011 в 19:26
  • Это не подведет! Я вызываю t.GetInterfaces () для типов. Я использую что-то подобное в моем проекте. –  Aliostad 25.02.2011 в 20:15