c# documentation example - Получение всех типов, реализующих интерфейс
Чтобы найти все типы в сборке, которые реализуют интерфейс IFoo:
var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type;
Обратите внимание, что предложение Райана Ринальди было неправильным. Он вернет 0 типов. Вы не можете писать
where type is IFoo
потому что тип является экземпляром System.Type и никогда не будет иметь тип IFoo. Вместо этого вы проверяете, можно ли назначить IFoo из этого типа. Это даст вам ожидаемые результаты.
Кроме того, предложение Адама Райта, которое в настоящее время отмечено как ответ, неверно и по той же причине. Во время выполнения вы увидите, что возвращается 0 типов, потому что все экземпляры System.Type не были разработчиками IFoo.
Используя отражение, как я могу получить все типы, которые реализуют интерфейс с C # 3.0 / .NET 3.5 с наименьшим кодом и минимизацию итераций?
Это то, что я хочу переписать:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
Я ценю, что это очень старый вопрос, но я думал, что добавлю еще один ответ будущим пользователям, поскольку все ответы на сегодняшний день используют некоторую форму Assembly.GetTypes
.
Хотя GetTypes () действительно вернет все типы, это не обязательно означает, что вы могли бы активировать их и, таким образом, могли бы вызвать исключение ReflectionTypeLoadException
.
Классическим примером неспособности активировать тип было бы, когда возвращаемый тип derived
из base
но base
определяется в другой сборке, а не в 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);
Другими словами, ClassB
не загружается, что-то, что вызов GetTypes проверяет и бросает.
Таким образом, чтобы безопасно квалифицировать набор результатов для загружаемых типов, то согласно этой статье « Фил Хаукед» « Получить все типы в сборке» и « Джон Скит» вы вместо этого сделаете что-то вроде:
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();
}
перебирать все загруженные сборки, циклически перебирать все их типы и проверять, реализуют ли они интерфейс.
что-то вроде:
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
}
}
}
Изменить: я только что видел редактирование, чтобы уточнить, что исходный вопрос был для сокращения итераций / кода, и все это хорошо и хорошо, как упражнение, но в реальных ситуациях вам понадобится самая быстрая реализация, независимо как классно выглядит 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;
}
Соглашусь, это некрасиво.
Другой ответ не работал с общим интерфейсом .
Это делает, просто замените 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;
Но действительно ли это более читаемо?