使用反射,如何以最少的代码获取使用 C# 3.0/.NET 3.5 实现接口的所有类型,并最小化迭代?

这就是我想重写的内容:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
有帮助吗?

解决方案

我的在 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
 }

要查找程序集中实现 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 是否可以从该类型分配。这将得到你预期的结果。

此外,亚当·赖特 (Adam Wright) 的建议(目前标记为答案)也是不正确的,原因相同。在运行时,您将看到返回 0 个类型,因为所有 System.Type 实例都不是 IFoo 实现者。

我很欣赏这是一个非常古老的问题,但我想我会为未来的用户添加另一个答案,因为迄今为止的所有答案都使用某种形式 Assembly.GetTypes.

虽然 GetTypes() 确实会返回所有类型,但这并不一定意味着您可以激活它们,因此可能会抛出一个 ReflectionTypeLoadException.

无法激活类型的一个典型示例是返回的类型为 derivedbasebase 是在与以下不同的程序集中定义的 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.

这是因为没有参考 AssemblyAAssemblyC 你将无法:

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();
}

其他答案在这里使用 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); 

编辑:我刚刚看到编辑澄清最初的问题是为了减少迭代/代码,这作为练习很好,但在现实情况下,无论如何,你都需要最快的实现底层 LINQ 的外观很酷。

这是我的 Utils 方法,用于迭代加载的类型。它可以处理常规类和接口,如果您正在自己/第三方代码库中寻找实现,则 exceptSystemTypes 选项可以极大地加快速度。

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();

将它们列入列表中。

没有简单的方法(就性能而言)来做你想做的事情。

反射主要适用于程序集和类型,因此您必须获取程序集的所有类型并查询它们以获得正确的接口。这是一个例子:

Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);

这将为您提供在程序集 MyAssembly 中实现 IMyInterface 的所有类型

选择装配位置时效果更好。如果您知道所有实现的接口都在同一个 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();

作者:坎·比尔金

我在 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;
}

您可以使用一些 LINQ 来获取列表:

var types = from type in this.GetType().Assembly.GetTypes()
            where type is ISomeInterface
            select type;

但实际上,这样更具可读性吗?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top