【问题标题】:How to determine if a type implements an interface with C# reflection如何确定一个类型是否实现了带有 C# 反射的接口
【发布时间】:2011-06-25 04:35:24
【问题描述】:

C# 中的 reflection 是否提供了一种方法来确定某些给定的 System.Type 类型是否对某些接口进行建模?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);

【问题讨论】:

    标签: c# reflection interface


    【解决方案1】:

    你有几个选择:

    1. typeof(IMyInterface).IsAssignableFrom(typeof(MyType))

    2. typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))

    对于通用接口,它有点不同。

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

    【讨论】:

    • 请记住 typeof(IMyInterface).IsAssignableFrom(typeof(IMyInterface)) 也是如此,这可能会对您的代码产生意想不到的结果。
    • 很容易不注意并向后获取IsAssignableFrom 的参数。我现在会选择GetInterfaces:p
    • IsAssignableFrom(t1) 变体比我的代码中的 GetInterfaces().Contains(t2) 变体快约 3 倍。
    • @PierreArnaud:IsAssignableFrom 最终会调用 GetInterfaces,因此您的测试可能首先检查 GetInterfaces,然后检查 IsAssignable。那是因为 GetInterfaces 缓存了它的结果,所以第一次调用会花费更多
    • 对@Kosta 的回答稍作改动。使用 C# 6,我们可以使用 typeof(MyType).GetInterface(nameof(IMyInterface)) != null 来实现更好的类型安全和重构。
    【解决方案2】:

    使用Type.IsAssignableFrom:

    typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
    

    【讨论】:

      【解决方案3】:
      typeof(IMyInterface).IsAssignableFrom(someclass.GetType());
      

      typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
      

      【讨论】:

      • 如果你已经有一个类的实例,更好的方法就是someclass is IMyInterface,因为这根本不涉及反射成本。所以,虽然没有错,但它不是一个理想的方法。
      • @James - 同意。甚至 Resharper 也给出了同样的建议。
      • @JamesJ.ReganIV 您应该将其发布为答案,我几乎错过了您的评论
      • @reggaeguitar,谢谢,但评论没有回答原始问题。这个问题要求反射解决方案,我只是说在这个答案的第一种情况下,你确实有一个对象反射的实例不是理想的解决方案。
      • @JamesJ.ReganIV 实际上,is 检查继承层次结构的两个方向,而 IsAssignableFrom 只向上检查。另外,如果你有一个对象的实例,你应该调用IsInstanceOfType(它也只能向上看)。
      【解决方案4】:
      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 最终经过几次检查确实调用 获取接口。
      2. 它遍历本地数组,所以会有 没有边界检查。
      3. 它使用 == 运算符,该运算符定义为 类型,因此可能比 Equals 方法更安全(即 Contains 调用,最终会使用)。

      【讨论】:

      • +1 对于内容,我讨厌括号和埃及括号周围的空间。整个方法也可以写成:return type.GetInterfaces().Any(t => t == ifaceType);
      • Type.IsAssignableFrom() 内部行为与您的代码完全一样
      • 为什么不使用不使用 LINQ 的 type.GetInterfaces().Contains(ifaceType)。
      【解决方案5】:

      我刚刚做了:

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

      我刚才说的是Implements,因为这样更直观。我总是得到IsAssignableFrom 翻转。

      【讨论】:

      • 您可以通过 return typeof(I).IsInterface &amp;&amp; typeof(I).IsAssignableFrom(source); 对方法的任何“不正确”用法返回 false,即;将它与类类型而不是接口类型一起使用,或者如果类型参数不是接口,则抛出异常。尽管您可以争辩说派生类“实现”了它的父类...
      【解决方案6】:

      正如其他人已经提到的: 本杰明 2013 年 4 月 10 日 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>();
      

      【讨论】:

      • 有没有理由不直接把实现放在扩展方法中?我的意思是肯定这可以让你同时调用它,但是你为什么需要这样做呢?
      • @MarqueIV 很抱歉迟到了将近 2 年才回复你,好吧,我想这是一个旧的坏习惯,在扩展方法中包装辅助方法以避免重复代码,将编辑我的答案:)
      • @MarqueIV done plus 改变了我不使用 alias 的另一个坏习惯,即 Boolean => bool (我不知道为什么我曾经有一些严格的“花式”编码规则我还年轻)。
      【解决方案7】:

      如果您有类型或实例,您可以轻松检查它们是否支持特定接口。

      测试一个对象是否实现了某个接口:

      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
          }
      

      【讨论】:

        【解决方案8】:

        修改 Jeff 的答案以获得最佳性能(感谢 Pierre Arnaud 的性能测试):

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

        【讨论】:

          【解决方案9】:

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

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

          正如几个 cmets 中所述,IsAssignableFrom 可能会因为“向后”而令人困惑。

          【讨论】:

            【解决方案10】:

            搜索此内容的任何人都可能会发现以下扩展方法很有用:

            public static class TypeExtensions
            {
                public static bool ImplementsInterface(this Type type, Type @interface)
                {
                    if (type == null)
                    {
                        throw new ArgumentNullException(nameof(type));
                    }
            
                    if (@interface == null)
                    {
                        throw new ArgumentNullException(nameof(@interface));
                    }
            
                    var interfaces = type.GetInterfaces();
                    if (@interface.IsGenericTypeDefinition)
                    {
                        foreach (var item in interfaces)
                        {
                            if (item.IsConstructedGenericType && item.GetGenericTypeDefinition() == @interface)
                            {
                                return true;
                            }
                        }
                    }
                    else
                    {
                        foreach (var item in interfaces)
                        {
                            if (item == @interface)
                            {
                                return true;
                            }
                        }
                    }
            
                    return false;
                }
            }
            

            xunit 测试:

            public class TypeExtensionTests
            {
                [Theory]
                [InlineData(typeof(string), typeof(IList<int>), false)]
                [InlineData(typeof(List<>), typeof(IList<int>), false)]
                [InlineData(typeof(List<>), typeof(IList<>), true)]
                [InlineData(typeof(List<int>), typeof(IList<>), true)]
                [InlineData(typeof(List<int>), typeof(IList<int>), true)]
                [InlineData(typeof(List<int>), typeof(IList<string>), false)]
                public void ValidateTypeImplementsInterface(Type type, Type @interface, bool expect)
                {
                    var output = type.ImplementsInterface(@interface);
                    Assert.Equal(expect, output);
                }
            }
            

            【讨论】:

              【解决方案11】:

              IsAssignableFrom 现已移至TypeInfo

              typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());
              

              【讨论】:

                【解决方案12】:

                请注意,如果您有一个通用接口 IMyInterface&lt;T&gt;,那么这将始终返回 false

                  typeof(IMyInterface<>).IsAssignableFrom(typeof(MyType)) /* ALWAYS FALSE */
                

                这也不起作用:

                  typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface<>))  /* ALWAYS FALSE */
                

                但是,如果MyType 实现IMyInterface&lt;MyType&gt;,这将起作用并返回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<>)
                

                【讨论】:

                  【解决方案13】:

                  正确答案是

                  typeof(MyType).GetInterface(nameof(IMyInterface)) != null;
                  

                  然而,

                  typeof(MyType).IsAssignableFrom(typeof(IMyInterface));
                  

                  可能会返回错误的结果,如下面的字符串和 IConvertible 代码所示:

                      static void TestIConvertible()
                      {
                          string test = "test";
                          Type stringType = typeof(string); // or test.GetType();
                  
                          bool isConvertibleDirect = test is IConvertible;
                          bool isConvertibleTypeAssignable = stringType.IsAssignableFrom(typeof(IConvertible));
                          bool isConvertibleHasInterface = stringType.GetInterface(nameof(IConvertible)) != null;
                  
                          Console.WriteLine($"isConvertibleDirect: {isConvertibleDirect}");
                          Console.WriteLine($"isConvertibleTypeAssignable: {isConvertibleTypeAssignable}");
                          Console.WriteLine($"isConvertibleHasInterface: {isConvertibleHasInterface}");
                      }
                  

                  结果:

                   isConvertibleDirect: True
                   isConvertibleTypeAssignable: False
                   isConvertibleHasInterface: True
                  

                  【讨论】:

                  • 正如您在接受的答案中看到的那样,您交换了 IsAssignableFrom 的使用类型。就像 Benjamin 和 Ehouarn 警告的那样。
                  【解决方案14】:

                  怎么样

                  if(MyType as IMyInterface != null)
                  

                  ?

                  【讨论】:

                  • 当我有一个实例时,这很明显。当我有来自反射的类型时没有用
                  【解决方案15】:

                  怎么样

                  typeof(IWhatever).GetTypeInfo().IsInterface
                  

                  【讨论】:

                    【解决方案16】:

                    如果你不需要使用反射并且你有一个对象,你可以使用这个:

                    if(myObject is IMyInterface )
                    {
                     // it's implementing IMyInterface
                    }
                    

                    【讨论】:

                    • 您的解决方案仅适用于现有对象。它不适用于类型。此外,如果您有一个已创建的对象,您可以获取其类型并执行必要的检查。
                    猜你喜欢
                    • 2013-02-14
                    • 2010-10-04
                    • 2011-02-01
                    • 2012-08-22
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-01-22
                    • 1970-01-01
                    相关资源
                    最近更新 更多