使用反射,我如何才能得到所有实现C 3.0/.NET 3.5接口的类型,代码最少,迭代最少?
这就是我想要重写的内容:
1 2 3
| foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff |
我的应该是C 3.0中的这个。
1 2 3 4
| var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p)); |
基本上,最少的迭代次数总是:
1 2 3
| loop assemblies
loop types
see if implemented. |
这对我很有用。它循环访问类并检查它们是否从MyInterface中脱离。
1 2 3 4
| foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
} |
要在实现ifoo接口的程序集中查找所有类型,请执行以下操作:
1 2 3
| var results = from type in someAssembly.GetTypes()
where typeof(IFoo).IsAssignableFrom(type)
select type; |
注意Ryan Rinaldi的建议是错误的。它将返回0个类型。你不能写
因为类型是System.Type实例,并且永远不会是ifoo类型。相反,您要检查ifoo是否可以从类型中赋值。这将得到你期望的结果。
另外,亚当·赖特的建议(目前标记为答案)也是不正确的,原因也是一样的。在运行时,您将看到0类型返回,因为所有System.Type实例都不是IFOO实现者。
我很感激这是一个非常古老的问题,但我想我会为未来的用户添加另一个答案,因为迄今为止所有的答案都使用某种形式的Assembly.GetTypes。
虽然gettypes()确实会返回所有类型,但并不一定意味着您可以激活它们,从而可能抛出ReflectionTypeLoadException。
无法激活类型的一个典型例子是,当返回的类型是来自base的derived,但base在不同于derived的程序集中定义,调用程序集不引用该程序集。
所以说我们有:
1 2 3
| Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA |
如果在ClassC中,即在AssemblyC中,我们按照接受的答案进行处理:
1 2 3 4
| var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p)); |
然后它会抛出一个ReflectionTypeLoadException。
这是因为如果没有在AssemblyC中提及AssemblyA,您将无法:
1 2
| var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType); |
换句话说,ClassB是不可加载的,这是gettypes调用检查和抛出的东西。
因此,为了安全地限定可加载类型的结果集,然后根据phil haacked的这篇文章获取程序集中的所有类型和jon skeet代码,您将改为执行如下操作:
1 2 3 4 5 6 7 8 9 10
| 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);
}
}
} |
然后:
1 2 3 4
| private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
var it = typeof (IMyInterface);
return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
} |
这里的其他答案使用IsAssignableFrom。您还可以使用System名称空间中的FindInterfaces,如本文所述。
下面是一个检查当前正在执行的程序集文件夹中所有程序集的示例,查找实现特定接口的类(为了清晰起见,避免使用LINQ)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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();
} |
如果要匹配多个接口,可以设置接口列表。
遍历所有加载的程序集,遍历所有类型,并检查它们是否实现接口。
类似:
1 2 3 4 5 6 7 8
| 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
}
}
} |
这对我很有效(如果希望在查找中排除系统类型):
1 2 3
| Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
t => lookupType.IsAssignableFrom(t) && !t.IsInterface); |
编辑:我刚刚看到编辑,澄清了最初的问题是为了减少迭代/代码,这是一个很好的练习,但在现实情况下,你会想要最快的实现,不管基础LINQ看起来有多酷。
下面是我的utils方法,用于遍历加载的类型。它处理常规类和接口,如果您在自己的/第三方代码库中寻找实现,excludeSystemTypes选项可以极大地加快速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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)。
1 2 3
| 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(); |
所以用
1
| AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()) |
我们得到所有的集会
1
| !x.IsInterface && !x.IsAbstract |
用于排除接口和抽象接口以及
1
| .Select(x => x.Name).ToList(); |
把它们列在清单上。
要想做你想做的事,没有一个简单的方法(就性能而言)。
反射主要与程序集和类型一起工作,因此您必须获取程序集的所有类型,并查询它们以获得正确的接口。下面是一个例子:
1 2 3
| Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null); |
这将获得在程序集myassembly中实现imyinterface的所有类型
在选择装配位置时更好。如果知道所有实现的接口都在同一assembly.definedtype中,则筛选大多数程序集。
1 2 3 4 5
| // 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(); |
罐头布尔金
我在LINQ代码中得到了异常,所以我这样做(没有复杂的扩展):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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;
} |
您可以使用一些LINQ来获取列表:
1 2 3
| var types = from type in this.GetType().Assembly.GetTypes()
where type is ISomeInterface
select type; |
但是,真的,这更易读吗?