Как с помощью отражения получить все типы, реализующие интерфейс с C# 3.0 / .NET 3.5, с наименьшим количеством кода и минимизацией итераций?
Вот что я хочу переписать:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
Оператор if в приведенном выше коде всегда будет ложным, потому что вы проверяете, реализует ли экземпляр класса Type (t) ваш интерфейс, чего он не будет, если Type не наследует IMyInterface (в этом случае он всегда будет истинным).





перебрать все загруженные сборки, перебрать все их типы и проверить, реализуют ли они интерфейс.
что-то вроде:
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
}
}
}
У меня было бы это в 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.
Обратите внимание, что список может также включать сам интерфейс. Измените последнюю строку на .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);, чтобы отфильтровать ее (или p.IsClass).
Примечание. Этот ответ неверен! Это проверяет «Совместимость назначений», а не реализован ли интерфейс. Например, List<string> не реализует IEnumerable<object>, но этот метод вернет true в .Net 4.0 из-за ковариации, что действительно неверно. Правильный ответ здесь
@SriramSakthivel во-первых, общие значения не указаны. Во-вторых, этот вопрос предшествует ковариации. В-третьих, вы делаете предположение, что они не хотят ковариантной отдачи.
Вы абсолютно правы, Даррен, я знаю, что это старая ветка, я только что зарегистрировал свой комментарий, чтобы будущие пользователи знали о существовании такой проблемы. Чтобы не обидеть вас. и, как говорится в заголовке вопроса, если OP запрашивает Получение всех типов, реализующих интерфейс, этот код этого не делает. но почти во всех случаях оно работает, без сомнения. как я уже сказал, есть и угловые случаи. Просто чтобы осознавать это;
Кроме того, нет необходимости объявлять var type = typeof(IMyInterface), потому что typeof(IMyInterface) оценивается во время компиляции и не повторно оценивается снова и снова в операторе Where.
Также необходимо убедиться, что класс не абстрактный => .Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
@Jonesopolis, задающий вопрос, не указал, хотят ли они создать экземпляр класса. абстрактные классы могут по-прежнему реализовывать интерфейсы и могут быть тем, что вы хотите искать, в зависимости от того, что вы делаете.
@DarrenKopp хороший звонок. Я только что сделал предположение, что он ищет экземпляры классов, я был, когда нашел этот вопрос.
убедитесь, что ваша сборка с интерфейсом включена в папку bin, чтобы этот код мог ее подобрать, это была моя проблема
Я пробовал это для классов, реализующих из Интерфейса. Работает нормально. Но почему это не работает для абстрактного класса?
Добавьте использование: using System;, using System.Reflection и using System.Linq для начинающих.
@SriramSakthivel Звучит непонятно, к сожалению, поискав в Google, я ничего не смог найти. Не могли бы вы назвать мне несколько ключевых слов для ковариации, чтобы я мог их найти?
@snowflake Статья Эрика должна помочь blogs.msdn.microsoft.com/ericlippert/2009/11/30/…. Google с такими ключевыми словами, как "ковариация Эрика Липперта", вы получите много просмотров.
Я хотел бы добавить, что проверка того, не является ли тип абстрактным, исключает как интерфейсы, так и абстрактные классы, поэтому .Where(p => type.IsAssignableFrom(p) && !p.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;
}
Признаюсь, это некрасиво.
Перечислители реализуют IDisposable, который не удаляется в try / finally. Лучше использовать foreach или linq.
Почему вы тестируете excludeSystemTypes дважды в одном if?
Нет простого способа (с точки зрения производительности) делать то, что вы хотите.
Отражение работает в основном со сборками и типами, поэтому вам нужно будет получить все типы сборки и запросить у них правильный интерфейс. Вот пример:
Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
Это даст вам все типы, реализующие IMyInterface в сборке MyAssembly.
Вы можете использовать LINQ для получения списка:
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
Но действительно ли это более читабельно?
Он мог бы быть более читабельным, если бы работал. К сожалению, ваше предложение where проверяет, реализует ли экземпляр класса System.Type ISomeInterface, что никогда не будет истинным, если только ISomeInterface действительно не является IReflect или ICustomAttributeProvider, и в этом случае он всегда будет истинным.
В приведенном выше ответе Карла Наяка есть ответ на исправление предложения where: IsAssignableFrom. Легкая ошибка за ответ.
Чтобы найти все типы в сборке, реализующие интерфейс 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.
Это сработало для меня (если вы хотите, вы можете исключить типы систем в поиске):
Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
t => lookupType.IsAssignableFrom(t) && !t.IsInterface);
В других ответах здесь используется 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();
}
Вы можете настроить список интерфейсов, если хотите сопоставить более одного.
Он ищет имя строкового интерфейса, которое я искал.
Работает при загрузке сборки в другом домене, так как тип должен быть сериализован в строку. очень здорово!
Я получаю: Не удается разрешить зависимость от сборки «System.Core, Version = 4.0.0.0, Culture = нейтральный, PublicKeyToken = b77a5c561934e089», потому что она не была предварительно загружена. При использовании API ReflectionOnly зависимые сборки должны быть предварительно загружены или загружены по запросу через событие ReflectionOnlyAssemblyResolve.
Это сработало для меня. Он просматривает классы и проверяет, получены ли они из myInterface.
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
Вы предполагаете, что сборка находится в основном исполняемом файле. Не дополнительный проект. Вы также повторяете без необходимости, несмотря на кучу итераций. Лучше, чтобы тяжелая работа выполняла каркас. Затем отфильтруйте дальше, когда найдете. Если уместно, обновите свой ответ. Включить List
Я понимаю, что это очень старый вопрос, но я подумал, что добавлю еще один ответ для будущих пользователей, поскольку все ответы на сегодняшний день используют некоторую форму 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();
}
Это помогло мне справиться с очень странной проблемой, когда в моем тестовом проекте GetTypes не работал и только в нашей CI-среде. GetLoadableTypes был исправлением для этого решения. Ошибка не могла быть воспроизведена в локальной среде, и это было следующее: System.Reflection.ReflectionTypeLoadException: невозможно загрузить один или несколько запрошенных типов. Получите свойство LoaderExceptions для получения дополнительных сведений. В частности, он жаловался на то, что существует тип, который не имеет конкретной реализации, и это произошло в проекте модульного тестирования. Спасибо за это!
Этот ответ должен быть отмечен как решение, он спас мою задницу сегодня, потому что, как сказал @Lari Tuomisto, на локальном env мы не могли повторно воспроизвести аналогичную ошибку
На случай, если это поможет кому-то другому: это решение сработало для меня, но мне пришлось изменить его, чтобы удалить тип интерфейса из списка. Я хотел активировать CreateInstance для всех из них, и возникло исключение, когда он пытался создать реальный интерфейс (что меня на некоторое время смутило, когда я подумал, что реальный интерфейс не в этом решении). Поэтому я изменил код на GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
У меня есть исключения в linq-коде, поэтому я делаю это так (без сложного расширения):
private static IList<Type> loadAllImplementingTypes(Type[] interfaces)
{
IList<Type> implementingTypes = 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)
implementingTypes.Add(currentType);
}
catch { }
return implementingTypes;
}
Другой ответ не работал с общий интерфейс.
Вот этот, просто замените 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();
иметь их в списке.
Пожалуйста, объясните, как работает ваше решение и почему оно превосходит все остальные ответы.
Это не выше или ниже, другие ответы не помогли мне, и я потрудился поделиться ими.
Мой комментарий касался только того, что ваш ответ - это код, поэтому я попросил вас добавить некоторые пояснения.
Еще лучше при выборе места сборки. Отфильтруйте большинство сборок, если вы знаете, что все реализованные вами интерфейсы находятся в пределах одного 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();
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));
}
Метод OfType Linq можно использовать именно для таких сценариев:
https://docs.microsoft.com/fr-fr/dotnet/api/system.linq.enumerable.oftype?view=netframework-4.8
Уже есть много правильных ответов, но я хотел бы добавить другую реализацию в виде расширения типа и список модульных тестов для демонстрации различных сценариев:
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 { }
}
Я вижу здесь так много чрезмерно сложных ответов, и люди всегда говорят мне, что я склонен к чрезмерному усложнению. Также неправильно использовать метод IsAssignableFrom для решения проблемы OP!
Вот мой пример, он выбирает все сборки из домена приложения, затем берет плоский список всех доступных типов и проверяет список интерфейсов каждого отдельного типа на соответствие:
public static IEnumerable<Type> GetImplementingTypes(this Type itype)
=> AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
.Where(t => t.GetInterfaces().Contains(itype));
Пример кода работает? У меня ложноотрицательные результаты с вашим условием if.