Получение всех типов, реализующих интерфейс
используя отражение, как я могу получить все типы, которые реализуют интерфейс с C# 3.0 / .NET 3.5 с наименьшим кодом и минимизацией итераций?
Это то, что я хочу заново написать:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
12 ответов
мой будет это в c# 3.0:)
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
в принципе, наименьшее количество итераций всегда будет:
loop assemblies
loop types
see if implemented.
чтобы найти все типы в сборке, которые реализуют интерфейс IFoo:
var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type;
where type is IFoo
потому что type-это система.Введите экземпляр и никогда не будет иметь тип IFoo. Вместо этого вы проверяете, можно ли назначить IFoo из типа. Это даст ожидаемые результаты.
кроме того, предложение Адама Райта, которое в настоящее время отмечено как ответ, неверно, как ну и по той же причине. Во время выполнения вы увидите, что 0 типов возвращаются, потому что вся система.Экземпляры типов не были реализаторами IFoo.
это сработало для меня. Это петли, хотя классы и проверяет, чтобы увидеть, если они которые можно вывести из myInterface
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
я ценю, что это очень старый вопрос, но я думал, что добавлю еще один ответ для будущих пользователей, поскольку все ответы на сегодняшний день используют некоторую форму Assembly.GetTypes
.
в то время как GetTypes() действительно вернет все типы, это не обязательно означает, что вы можете активировать их и, таким образом, потенциально бросить ReflectionTypeLoadException
.
классический пример невозможности активировать тип будет, когда тип возвращается derived
С base
но!--8--> определен в другой сборке от derived
, сборка, на которую вызывающая сборка не ссылается.
так скажем у нас:
Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA
если в ClassC
который находится в AssemblyC
затем мы делаем что-то в соответствии с принятым ответом:
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
тогда он бросит ReflectionTypeLoadException
.
это потому что без ссылки на AssemblyA
на AssemblyC
вы не сможете:
var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
In другие слова ClassB
не загружаемый который является то, что вызов GetTypes проверяет и бросает на.
так смело претендовать результирующий набор подгружаемых типов, тогда как за этот Фил Haacked статьи получить все типы в сборке и Джон Скит код вы бы вместо этого сделать что-то вроде:
public static class TypeLoaderExtensions {
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null) throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
} catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
и затем:
private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
var it = typeof (IMyInterface);
return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
другие ответы здесь использовать IsAssignableFrom
. Вы также можете использовать FindInterfaces
С System
пространства имен, как описано здесь.
вот пример, который проверяет все сборки в папке текущей выполняемой сборки, ища классы, реализующие определенный интерфейс (избегая LINQ для ясности).
static void Main() {
const string qualifiedInterfaceName = "Interfaces.IMyInterface";
var interfaceFilter = new TypeFilter(InterfaceFilter);
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var di = new DirectoryInfo(path);
foreach (var file in di.GetFiles("*.dll")) {
try {
var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
foreach (var type in nextAssembly.GetTypes()) {
var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
if (myInterfaces.Length > 0) {
// This class implements the interface
}
}
} catch (BadImageFormatException) {
// Not a .net assembly - ignore
}
}
}
public static bool InterfaceFilter(Type typeObj, Object criteriaObj) {
return typeObj.ToString() == criteriaObj.ToString();
}
вы можете настроить список интерфейсов, если хотите сопоставить более одного.
цикл через все загруженные сборки, цикл через все их типы и проверьте, реализуют ли они интерфейс.
что-то типа:
Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
foreach (Type t in asm.GetTypes()) {
if (ti.IsAssignableFrom(t)) {
// here's your type in t
}
}
}
это сработало для меня (если вы хотите, чтобы вы могли исключить системные типы в поиске):
Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
t => lookupType.IsAssignableFrom(t) && !t.IsInterface);
Edit: я только что видел редактирование, чтобы уточнить, что исходный вопрос был для сокращения итераций / кода, и это все хорошо и хорошо как упражнение, но в реальных ситуациях вам понадобится самая быстрая реализация, независимо от того, насколько круто выглядит базовый LINQ.
вот мой метод Utils для итерации через загруженные типы. Он обрабатывает регулярные классы, а также интерфейсы, и опция excludeSystemTypes значительно ускоряет работу, если вы поиск реализаций в вашей собственной / сторонней кодовой базе.
public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) {
List<Type> list = new List<Type>();
IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
while (enumerator.MoveNext()) {
try {
Type[] types = ((Assembly) enumerator.Current).GetTypes();
if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) {
IEnumerator enumerator2 = types.GetEnumerator();
while (enumerator2.MoveNext()) {
Type current = (Type) enumerator2.Current;
if (type.IsInterface) {
if (current.GetInterface(type.FullName) != null) {
list.Add(current);
}
} else if (current.IsSubclassOf(type)) {
list.Add(current);
}
}
}
} catch {
}
}
return list;
}
нет простого способа (с точки зрения производительности) сделать то, что вы хотите сделать.
отражение работает с ассемблерами и типами в основном, поэтому вам придется получить все типы сборки и запросить их для правильного интерфейса. Вот пример:
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
Это даст вам все типы, которые реализуют IMyInterface в сборке MyAssembly
У меня есть исключения в linq-коде, поэтому я делаю это так (без сложного расширения):
private static IList<Type> loadAllTypes(Types[] interfaces)
{
IList<Type> objects = new List<Type>();
// find all types
foreach (var interfaceType in interfaces)
foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies())
try
{
foreach (var currentType in currentAsm.GetTypes())
if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract)
objects.Add(currentType);
}
catch { }
return objects;
}
другой ответ не работал с универсальный интерфейс.
это так, просто замените typeof (ISomeInterface) на typeof (T).
List<string> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.Where(x => typeof(ISomeInterface).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.Select(x => x.Name).ToList();
поэтому с
AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
мы получаем все сборки
!x.IsInterface && !x.IsAbstract
используется для исключения интерфейса и абстрактных и
.Select(x => x.Name).ToList();
чтобы иметь их в список.
вы можете использовать LINQ для получения списка:
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
но на самом деле, это более читабельным?