使用反射,如何以最少的代码获得使用C#3.0 / .NET 3.5实现接口的所有类型,并最大程度地减少迭代次数?
这是我要重写的内容:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
使用反射,如何以最少的代码获得使用C#3.0 / .NET 3.5实现接口的所有类型,并最大程度地减少迭代次数?
这是我要重写的内容:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
Answers:
我的将是在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>
但由于协方差,该方法在.Net 4.0中将返回true,这的确是错误的。正确的答案在这里
.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
这对我有用。它循环遍历这些类,并检查它们是否源自myInterface
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
要在实现IFoo接口的程序集中查找所有类型,请执行以下操作:
var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type;
请注意,Ryan Rinaldi的建议是不正确的。它将返回0个类型。你不能写
where type is IFoo
因为type是System.Type实例,所以永远不会是IFoo类型。而是,检查是否可以从该类型分配IFoo。那会得到您预期的结果。
同样,出于同样的原因,目前被标记为答案的亚当·赖特的建议也是错误的。在运行时,您会看到0类型返回,因为所有System.Type实例都不是IFoo实现者。
我很欣赏这是一个非常老的问题,但我想我会为将来的用户添加另一个答案,因为迄今为止所有答案都使用某种形式的 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
in 的引用,AssemblyC
您将无法:
var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
换句话说ClassB
是不可加载的,这是对GetTypes的调用检查并抛出的错误。
因此,为了安全地将结果集限定为可装入类型,然后按照此Phil Haacked文章“在Assembly中获取所有类型”和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();
}
CreateInstance
为所有它们激活,并且在尝试创建实际接口时引发了异常(当我认为实际接口不适合该解决方案时,这让我感到困惑)。因此,我将代码更改为GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).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();
}
如果要匹配多个接口,可以设置接口列表。
编辑:我刚刚看过该编辑,以澄清原来的问题是减少迭代/代码,并且作为练习很好,但是在现实世界中,无论哪种情况,您都想要最快的实现底层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;
}
我承认,这并不漂亮。
excludeSystemTypes
两次测试if
?
其他答案不适用于通用接口。
这确实可以,只需将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();
已经有许多有效的答案,但是我想添加另一种实现作为Type扩展和一系列单元测试来演示不同的场景:
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 { }
}
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代码中遇到了异常,因此我采用这种方式(没有复杂的扩展名):
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;
}
您可以使用一些LINQ来获取列表:
var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type;
但是真的,这更具可读性吗?