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