Подтвердить что ты не робот

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

У меня есть интерфейс 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[] для конструктора на последней строке в выдержке. Я пробовал разные способы его бросить, но никто не работает.

4b9b3361

Ответ 1

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

Ответ 2

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

Ответ 3

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

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

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>();

Ответ 4

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