如何用C#反射确定一个类型是否实现了接口

712

C#中,反射是否提供了一种确定某个给定的System.Type类型是否模拟某个接口的方法?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);
16个回答

1232

你有几个选择:

  1. typeof(IMyInterface).IsAssignableFrom(typeof(MyType))
  2. typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))
  3. 在C# 6中,你可以使用typeof(MyType).GetInterface(nameof(IMyInterface)) != null

对于泛型接口,情况略有不同。

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))

84
请记住,typeof(IMyInterface).IsAssignableFrom(typeof(IMyInterface)) 也是成立的,这可能会对您的代码产生意想不到的影响。 - Chris Kemp
34
没注意就把“IsAssignableFrom”的参数搞反了确实很容易,现在我会选用“GetInterfaces”啦 :p - Benjamin
18
在我的代码中,IsAssignableFrom(t1) 变体比 GetInterfaces().Contains(t2) 相对应的方法快大约3倍。 - Pierre Arnaud
33
“IsAssignableFrom” 最终会调用 “GetInterfaces”,所以你的测试可能先检查了 “GetInterfaces”,然后才是 “IsAssignableFrom”。这是因为 “GetInterfaces” 会缓存其结果,因此第一次调用的成本较高。 - Panos Theof
24
@Kosta的回答可以做一个小改动。在C# 6中,我们可以使用typeof(MyType).GetInterface(nameof(IMyInterface)) != null来获得更好的类型安全性和重构能力。 - aholmes
显示剩余8条评论

111

36
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());
或者
typeof(IMyInterface).IsAssignableFrom(typeof(MyType));

46
如果您已经有了一个类的实例,更好的方法是简单地使用 someclass is IMyInterface,因为这根本不涉及反射的成本。 因此,虽然不算错误,但这不是一种理想的方式。 - Arrya Regan
2
@James - 同意。即使是 Resharper 也给出了相同的建议。 - Angshuman Agarwal
1
@reggaeguitar,谢谢你的回复,但是它并没有回答原来的问题。问题要求反射解决方案,我只是在这个答案的第一个情况中说,当您确实拥有对象实例时,反射不是理想的解决方案。 - Arrya Regan
1
@JamesJ.ReganIV 实际上,is 检查继承层次结构的两个方向,而 IsAssignableFrom 仅向上检查。此外,如果您有一个对象实例,应该调用 IsInstanceOfType(它也只向上查找)。 - Sellorio
@MrUniverse is只在一个方向上进行检查,否则new object() is string将会是真。 - Jon Hanna
显示剩余2条评论

26

使用 Type.IsAssignableTo (从 .NET 5.0 开始):

typeof(MyType).IsAssignableTo(typeof(IMyInterface));

如几条评论所述,IsAssignableFrom可能因为“反向”的原因而被认为是令人困惑的。


这也捕获基类以及接口(对我来说很好 :))。 - Slate

15
public static bool ImplementsInterface(this Type type, Type ifaceType) 
{
    Type[] intf = type.GetInterfaces();
    for(int i = 0; i < intf.Length; i++) 
    {
        if(intf[ i ] == ifaceType) 
        {
            return true;
        }
    }
    return false;
}

我认为这是正确的版本,有三个原因:

  1. 它使用GetInterfaces而不是IsAssignableFrom,因为IsAssignableFrom最终经过几次检查后会调用GetInterfaces,所以它更快。
  2. 它遍历本地数组,因此不会进行边界检查。
  3. 它使用==运算符,该运算符已定义为Type类型,因此可能比Equals方法更安全(Contains调用将最终使用该方法)。

10
对于内容给予 +1,但我不喜欢圆括号周围的空格和埃及大括号。整个方法也可以简化为:return type.GetInterfaces().Any(t => t == ifaceType);。请注意,我的翻译只涉及到对原文的转换,没有额外的解释或其他信息返回。 - reggaeguitar
1
Type.IsAssignableFrom() 内部的行为与您的代码完全相同。 - gdbdable
1
另外,为什么不使用 LINQ,而是使用 type.GetInterfaces().Contains(ifaceType)? - user3638471

12
如果您有一个类型或实例,您可以轻松地检查它们是否支持特定的接口。
要测试对象是否实现了某个特定接口:
if(myObject is IMyInterface) {
  // object myObject implements IMyInterface
}

要测试一个类型是否实现了某个接口:

if(typeof(IMyInterface).IsAssignableFrom(typeof(MyType))) {
  // type MyType implements IMyInterface
}

如果你有一个通用对象,并且想要进行强制转换以及检查被转换到的接口是否已经被实现,那么代码如下:

 var myCastedObject = myObject as IMyInterface;

    if(myCastedObject != null) {
      // object myObject implements IMyInterface
    }

最后一个例子可以简化为 if (myObject is IMyInterface myCastedObject) { ... } - dimitar.bogdanov
最后一个例子可以简化为 if (myObject is IMyInterface myCastedObject) { ... } - undefined

10

我刚刚做了:

public static bool Implements<I>(this Type source) where I : class
{
  return typeof(I).IsAssignableFrom(source);
}

我希望我可以写成where I : Interface,但是Interface不是一种通用参数约束选项。使用class是最接近的方式。

用法:

if(MyType.Implements<IInitializable>())
  MyCollection.Initialize();

我只是说“实现”因为那更直观。我总是混淆IsAssignableFrom


您可以使用return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source)来在方法的任何“不正确”使用上返回false,也就是说,在使用类类型而不是接口类型时,或者如果类型参数不是接口,则抛出异常。但是您可以争辩说派生类“实现”其父类... - Sindri Jóelsson

8

正如其他人已经提到的: Benjamin Apr 10 '13在22:21"

不注意并将IsAssignableFrom的参数弄反了确实很容易。我现在会使用GetInterfaces:p -

另一种方法是创建一个简短的扩展方法,以某种程度上满足“最常见”的思考方式(同意这是一个非常小的个人选择,使其稍微“更自然”,基于个人的偏好):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }
}

为什么不更泛化一些呢(不确定这是否真的有趣,但我认为我只是再添加一点“语法糖”):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }

    public static bool IsAssignableTo<TAssignable>(this Type type)
    {
        return IsAssignableTo(type, typeof(TAssignable));
    }
}

我认为这样可能更加自然,但再次强调,这只是个人意见:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();

4
你为什么不直接将实现放在扩展方法中?虽然这样可以让你两种方式都能调用它,但是你为什么需要这么做呢? - Mark A. Donohoe
@MarqueIV 很抱歉近两年才回复你,我想那时候将辅助方法包装成扩展方法以避免重复代码是一种旧的坏习惯,我会编辑我的答案 :) - Natalie Perret
1
@MarqueIV 完成了,还改掉了我另一个不好的习惯,即不使用别名,例如 Boolean => bool(我不知道为什么年轻时会有一些严格的“花哨”的编码规则)。 - Natalie Perret

7

针对 Pierre Arnaud 的性能测试结果,优化 Jeff 的答案,如下所示:

var type = typeof(MyType);
var implementsInterface = typeof(IMyInterface).IsAssignableFrom(type) && type.IsClass;

为了查找在给定的Assembly中实现某个接口的所有类型:
var implementations = typeof(TypeInTargetAssembly).Assembly.GetTypes()
                          .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && t.IsClass);

6
请注意,如果您有一个泛型接口 IMyInterface<T>,则此语句将始终返回 false:
  typeof(IMyInterface<>).IsAssignableFrom(typeof(MyType)) /* ALWAYS FALSE */

这也不起作用:
  typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface<>))  /* ALWAYS FALSE */

然而,如果MyType实现了IMyInterface<MyType>,则以下代码可以正常工作并返回true:
  typeof(IMyInterface<MyType>).IsAssignableFrom(typeof(MyType))

然而,在运行时,您可能不知道类型参数T。一个有点hacky(不太正式的)的解决方案是:

  typeof(MyType).GetInterfaces()
                .Any(x=>x.Name == typeof(IMyInterface<>).Name)

Jeff的解决方案稍微不太粗糙:

  typeof(MyType).GetInterfaces()
         .Any(i => i.IsGenericType 
             && i.GetGenericTypeDefinition() == typeof(IMyInterface<>));

这是一种适用于任何情况的Type扩展方法:

public static class TypeExtensions
{
    public static bool IsImplementing(this Type type, Type someInterface)
    {
        return type.GetInterfaces()
             .Any(i => i == someInterface 
                 || i.IsGenericType 
                    && i.GetGenericTypeDefinition() == someInterface);
    }
}

(请注意,上述使用的是 linq,可能比循环慢。)
您可以这样做:
   typeof(MyType).IsImplementing(IMyInterface<>)

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