Получение всех типов, которые реализуют интерфейс
Используя рефлексию, как я могу получить все типы, которые реализуют интерфейс с C# 3.0/.NET 3.5 с наименьшим количеством кода и минимизируя итерации?
Вот что я хочу переписать:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
19 ответов
Мой был бы это в 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.
Это сработало для меня. Он проходит через классы и проверяет, получены ли они из 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
но 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.
Таким образом, чтобы безопасно квалифицировать набор результатов для загружаемых типов, в соответствии с этой статьей Фила Хаака " Получить все типы в сборке и код Jon Skeet" вместо этого вы должны сделать что-то вроде:
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();
}
Чтобы найти все типы в сборке, реализующей интерфейс 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.
Другие ответы здесь используют 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);
Другой ответ не работал с универсальным интерфейсом.
Это делает, просто замените 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();
иметь их в списке.
Я вижу здесь так много чрезмерно сложных ответов, и люди всегда говорят мне, что я склонен к чрезмерному усложнению. Также используяIsAssignableFrom
метод решения проблемы OP неверен!
Вот мой пример, он выбирает все сборки из домена приложения, затем берет плоский список всех доступных типов и проверяет список интерфейсов каждого отдельного типа на соответствие:
public static IEnumerable<Type> GetImplementingTypes(this Type itype)
=> AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
.Where(t => t.GetInterfaces().Contains(itype));
Все ответы, опубликованные до сих пор, либо учитывают слишком мало, либо слишком много сборок. Вам нужно проверять только те сборки, которые ссылаются на сборку, содержащую интерфейс. Это сводит к минимуму количество ненужных статических конструкторов и экономит огромное количество времени и, возможно, непредвиденных побочных эффектов в случае сторонних сборок.
public static class ReflectionUtils
{
public static bool DoesTypeSupportInterface(Type type, Type inter)
{
if (inter.IsAssignableFrom(type))
return true;
if (type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == inter))
return true;
return false;
}
public static IEnumerable<Assembly> GetReferencingAssemblies(Assembly assembly)
{
return AppDomain
.CurrentDomain
.GetAssemblies().Where(asm => asm.GetReferencedAssemblies().Any(asmName => AssemblyName.ReferenceMatchesDefinition(asmName, assembly.GetName())));
}
public static IEnumerable<Type> TypesImplementingInterface(Type desiredType)
{
var assembliesToSearch = new Assembly[] { desiredType.Assembly }
.Concat(GetReferencingAssemblies(desiredType.Assembly));
return assembliesToSearch.SelectMany(assembly => assembly.GetTypes())
.Where(type => DoesTypeSupportInterface(type, desiredType));
}
public static IEnumerable<Type> NonAbstractTypesImplementingInterface(Type desiredType)
{
return TypesImplementingInterface(desiredType).Where(t => !t.IsAbstract);
}
}
Редактировать: я только что видел редактирование, чтобы уточнить, что исходный вопрос был для сокращения итераций / кода, и это все хорошо, как упражнение, но в реальных ситуациях вам понадобится самая быстрая реализация, независимо от того, о том, как здорово выглядит лежащий в основе 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.DefinedTypes.
// We get the assembly through the base class
var baseAssembly = typeof(baseClass).GetTypeInfo().Assembly;
// we filter the defined classes according to the interfaces they implement
var typeList = baseAssembly.DefinedTypes.Where(type => type.ImplementedInterfaces.Any(inter => inter == typeof(IMyInterface))).ToList();
Нет простого способа (с точки зрения производительности) сделать то, что вы хотите.
Reflection работает в основном со сборками и типами, поэтому вам нужно получить все типы сборок и запросить их для правильного интерфейса. Вот пример:
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
Это даст вам все типы, которые реализуют IMyInterface в сборке MyAssembly.
Если это кому-то поможет, это то, что я использую, чтобы упростить некоторые из моих модульных тестов :)
public static Type GetInterfacesImplementation(this Type type)
{
return type.Assembly.GetTypes()
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface)
.SingleOrDefault();
}
Уже есть много правильных ответов, но я хотел бы добавить другую реализацию в виде расширения типа и список модульных тестов для демонстрации различных сценариев:
public static class TypeExtensions
{
public static IEnumerable<Type> GetAllTypes(this Type type)
{
var typeInfo = type.GetTypeInfo();
var allTypes = GetAllImplementedTypes(type).Concat(typeInfo.ImplementedInterfaces);
return allTypes;
}
private static IEnumerable<Type> GetAllImplementedTypes(Type type)
{
yield return type;
var typeInfo = type.GetTypeInfo();
var baseType = typeInfo.BaseType;
if (baseType != null)
{
foreach (var foundType in GetAllImplementedTypes(baseType))
{
yield return foundType;
}
}
}
}
Этот алгоритм поддерживает следующие сценарии:
public static class GetAllTypesTests
{
public class Given_A_Sample_Standalone_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleStandalone);
_expectedTypes =
new List<Type>
{
typeof(SampleStandalone),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Abstract_Base_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleBase);
_expectedTypes =
new List<Type>
{
typeof(SampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Child_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleChild);
_expectedTypes =
new List<Type>
{
typeof(SampleChild),
typeof(SampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Base_Interface_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(ISampleBase);
_expectedTypes =
new List<Type>
{
typeof(ISampleBase)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Child_Interface_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(ISampleChild);
_expectedTypes =
new List<Type>
{
typeof(ISampleBase),
typeof(ISampleChild)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Implementation_Class_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
protected override void Given()
{
_sut = typeof(SampleImplementation);
_expectedTypes =
new List<Type>
{
typeof(SampleImplementation),
typeof(SampleChild),
typeof(SampleBase),
typeof(ISampleChild),
typeof(ISampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
public class Given_A_Sample_Interface_Instance_Type_When_Getting_All_Types
: Given_When_Then_Test
{
private Type _sut;
private IEnumerable<Type> _expectedTypes;
private IEnumerable<Type> _result;
class Foo : ISampleChild { }
protected override void Given()
{
var foo = new Foo();
_sut = foo.GetType();
_expectedTypes =
new List<Type>
{
typeof(Foo),
typeof(ISampleChild),
typeof(ISampleBase),
typeof(object)
};
}
protected override void When()
{
_result = _sut.GetAllTypes();
}
[Fact]
public void Then_It_Should_Return_The_Right_Type()
{
_result.Should().BeEquivalentTo(_expectedTypes);
}
}
sealed class SampleStandalone { }
abstract class SampleBase { }
class SampleChild : SampleBase { }
interface ISampleBase { }
interface ISampleChild : ISampleBase { }
class SampleImplementation : SampleChild, ISampleChild { }
}
Я получил исключения в 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;
}
Метод OfType Linq можно использовать именно для таких сценариев:
https://docs.microsoft.com/fr-fr/dotnet/api/system.linq.enumerable.oftype?view=netframework-4.8
public IList<T> GetClassByType<T>()
{
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.ToList(p => typeof(T)
.IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
.SelectList(c => (T)Activator.CreateInstance(c));
}
Вы можете использовать LINQ, чтобы получить список:
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
Но действительно ли это более читабельно?