获取实现一个接口的所有类型

662
使用反射,如何用最少的代码和迭代次数获取在C# 3.0/.NET 3.5中实现一个接口的所有类型?请注意保留HTML标签。
foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff

1
示例代码是否有效?使用您的if条件语句时,我得到了错误的负面结果。 - Emperor Orionii
5
以上代码中的if语句永远会是false,因为你正在测试Type类的一个实例(t)是否实现了你的接口,除非Type继承IMyInterface接口(在这种情况下它将永远为true),否则它不会实现该接口。 - Liazy
19个回答

943

我的选择是这个,使用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.

254
请注意,列表中也可能包括接口本身。将最后一行更改为.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);以过滤掉它(或者使用p.IsClass)。 - jtpereyda
50
注意:这个回答是错误的!它检查的是“赋值兼容性”,而不是接口是否被实现。例如,List<string>没有实现IEnumerable<object>,但是由于.NET 4.0中支持协变,此方法将返回true,这是错误的。正确答案在这里 - Sriram Sakthivel
25
首先,未指定通用值。其次,这个问题早于协变。第三,你假设协变返回不是他们想要的东西。 - Darren Kopp
32
你说得完全正确,Darren。我知道这个帖子很旧了,但我刚刚注册评论,是为了让未来的用户意识到存在这样的问题,而不是冒犯你。正如问题标题所说,如果 OP 正在询问“获取实现接口的所有类型”,那么这段代码并不能做到这一点。但几乎所有情况下它都有效,毫无疑问。还有一些极端情况,就像我之前说的一样。只是要注意一下这个问题; - Sriram Sakthivel
23
也需要确保该类不是抽象类 => .Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract - Jonesopolis
显示剩余15条评论

95

这对我有用。它循环遍历类并检查它们是否派生自myInterface。

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }

8
你假设这个程序集在主要可执行文件中,而不是另一个项目中。同时你也进行了不必要的迭代操作,更好的方式是让框架来完成重活。当找到后再进一步筛选。如果合适,请更新你的回答,包括使用List<T>的原因。以下是需要翻译的内容:"var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).Where(mytype => typeof(myInterface).IsAssignableFrom(mytype) && mytype.GetInterfaces().Contains(typeof(myInterface))); foreach(var item in items) Console.Log(item.Name);" - TamusJRoyce
即使我的汇编代码在另一个项目中,这对我仍然有效。但是,后来它给我带来了“InvalidCastException”的问题,因此如果确实想稍后将其转换为该接口,则使用.IsAssignableFrom(..)可能更安全。 - eternal_frame

83

其他答案使用了一些形式的Assembly.GetTypes

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

一个无法激活类型的经典例子是,当返回的类型从base派生而来,但base在与derived不同的程序集中定义,而调用程序集没有引用该程序集时。

所以说我们有:

Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA

如果在位于AssemblyC中的ClassC中,我们按照接受的答案执行某些操作:
var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

然后它会抛出一个ReflectionTypeLoadException

这是因为在AssemblyC中没有对AssemblyA的引用,你将无法:

var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);

换句话说,ClassB不可加载,这是调用GetTypes进行检查并抛出的内容。
因此,为了安全地限定可加载类型的结果集合,根据Phil Haacked文章Get All Types in an AssemblyJon Skeet code,你应该执行以下操作:
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();
}

4
这帮助我解决了一个超级奇怪的问题,在我的测试项目中,GetTypes会失败,而且只在我们的CI环境中出现问题。 GetLoadableTypes是这个问题的解决方法。错误无法在本地环境中重现,并且错误提示为:System.Reflection.ReflectionTypeLoadException:无法加载所请求的一个或多个类型。请检索LoaderExceptions属性以获取更多信息。更具体地说,它抱怨有一种类型没有具体实现,而这发生在单元测试项目中。感谢这个! - Lari Tuomisto
2
这个答案应该被标记为解决方案,它今天救了我的命,因为就像@Lari Tuomisto所说,在本地环境中我们无法重现类似的错误。 - Lightning3
4
如果有人需要的话:这个解决方案对我有用,但我不得不修改它以从列表中移除接口类型。我希望对所有类型都激活CreateInstance,但当它尝试创建实际接口时(当我认为实际接口已经在此解决方案中了一段时间)抛出了异常,这让我感到困惑。所以我将代码更改为 GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList(); - Xavier Peña

66
在程序集中查找所有实现IFoo接口的类型:
var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;
请注意,Ryan Rinaldi的建议是不正确的。它会返回0个类型。您不能编写:

Note that Ryan Rinaldi's suggestion was incorrect. It will return 0 types. You cannot write


where type is IFoo

因为 type 是 System.Type 的实例,并且永远不会是 IFoo 类型。相反,您需要检查 IFoo 是否可以从该类型分配。这将得到您期望的结果。

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


这是正确的答案。被接受的答案不起作用。 - Petter Pettersson
这也会返回接口本身。看起来问题只要求返回实现了该接口的类的类型,但不包括接口本身。最好的。 - JP Garza
同意。这是正确的答案。另外,由于我喜欢lambda语法,它可以写成:var results = assembly.GetTypes().Where(t => typeof(IFoo).IsAssignableFrom(t)) .Select(t => t).ToList(); - Paul Rouleau

23

这里的其他答案使用了IsAssignableFrom。你也可以使用System命名空间中的FindInterfaces,具体说明请参见此处

以下是一个示例,它检查当前执行程序集文件夹中的所有程序集,查找实现特定接口的类(为了清晰起见,避免使用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();
}

如果你想匹配多个接口,可以设置一个接口列表。


这个程序查找字符串接口名称,这正是我要找的。 - senthil
在加载不同域中的程序集时工作,因为类型必须序列化为字符串。非常棒! - TamusJRoyce
我收到以下错误信息:无法解析对程序集'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'的依赖项,因为它尚未被预加载。在使用ReflectionOnly API时,必须预加载或通过ReflectionOnlyAssemblyResolve事件按需加载依赖的程序集。 - bkwdesign

18

遍历所有已加载的程序集,循环遍历它们的类型,并检查它们是否实现了接口。

类似于以下代码:

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
        }
    }
}

12
这对我有用(如果你希望在查找中排除系统类型,可以这样做):
Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 

11

其他答案无法适用于通用接口。

这个可以,只需将 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();

So with

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

我们获取所有程序集。
!x.IsInterface && !x.IsAbstract

用于排除接口和抽象类。

.Select(x => x.Name).ToList();

将它们列在一个列表中。


请解释一下你的解决方案是如何工作的,以及为什么它比其他答案更优秀。 - Lukas Körfer
它并不是更好或更差,其他答案对我没有用,我费心分享了它。 - Antonin GAVREL
我的评论只是关于你的回答仅仅是代码,所以我要求你添加一些解释。 - Lukas Körfer

9

我看到这里有很多过于复杂的回答,人们总是告诉我我往往把事情复杂化了。此外,使用 IsAssignableFrom 方法解决 OP 问题是错误的!

下面是我的例子,它选择应用程序域中的所有程序集,然后获取所有可用类型的平面列表,并检查每个单独类型的接口列表以进行匹配:

public static IEnumerable<Type> GetImplementingTypes(this Type itype) 
    => AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
           .Where(t => t.GetInterfaces().Contains(itype));

6
到目前为止,所有已发布的答案都考虑了太少或太多的程序集。您只需要检查引用接口所在程序集的程序集。这将最小化运行不必要的静态构造函数的数量,并节省大量时间,在第三方程序集的情况下还可以避免可能出现的意外副作用。
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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接