Создание экземпляров всех классов, реализующих определенный интерфейс - PullRequest
17 голосов
/ 25 февраля 2011

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

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

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

Обновление: Я уже попробовал несколько предложенных подходов, но на всех из них, строка Activator.CreateInstance(type), я получаю Систему.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[] для конструктора наПоследняя строка в выдержке.Я пробовал разные способы его разыграть, но ни один из них не работает.

Ответы [ 4 ]

29 голосов
/ 25 февраля 2011

Это можно сделать с помощью 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. Это может отличаться от всех сборок, загруженных в текущем процессе. Однако для многих типов приложений это будет эквивалентно.

8 голосов
/ 25 февраля 2011

Вам нужно знать список сборок для поиска, но тогда 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>.

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

5 голосов
/ 25 февраля 2011

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

По ссылке выше;

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>();
0 голосов
/ 25 февраля 2011

Попробуйте использовать отражение, чтобы получить типы, реализующие 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));
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...