代码之家  ›  专栏  ›  技术社区  ›  LBushkin

使用反射区分重载方法的泛型和非泛型版本

  •  3
  • LBushkin  · 技术社区  · 16 年前

    我在使用反射来区分泛型类上的非泛型方法和泛型方法时遇到一些困难。下面是我正在处理的一个测试用例:

    public class Foo<T>
    {
      public string Bar( T value ) { return "Called Bar(T)"; }
    
      public string Bar( int value ) { return "Called Bar(int)"; }
    
      public static void CallBar<TR>(Foo<TR> foo)
      {
          var fooInfo = foo.GetType()
             .GetMethods()
             .Where(x => !x.IsGenericMethod && // doesn't filter out Bar(T)!
                     x.Name == "Bar" &&
                     x.GetParameters().First().ParameterType == typeof(int))
             // !Two identical MethodInfo results, how to choose between them?
             // Is there a gauranteed canonical ordering? Or is it undefined?
             .First();
    
          Console.WriteLine(fooInfo.Invoke(foo, new object[]{ 0 }));
      }
    }
    
    // prints Bar(T)...
    Foo<int>.CallBar( new Foo<int>() );
    
    5 回复  |  直到 16 年前
        1
  •  2
  •   Avish    16 年前

    不幸的是,System.Reflection并不能提供一种将已构造类型上的方法与从中构造该方法的泛型类型定义上的相应方法相关联的好方法。我知道有两种解决方案,都不是完美的:

    解决方案1:静态typebuilder.getmethod。 a static version of GetMethod on TypeBuilder 接受泛型类型定义上的方法的泛型构造类型和MethodInfo,并返回 指定的泛型类型上的对应方法。在本例中,调用 TypeBuilder.GetMethod(Foo<int>, Foo<T>.Bar(T)) 会给你 Foo<int>.Bar(T-as-int) 然后你可以用它来消除它和 Foo<int>.Bar(int) .

    (当然,上面的示例不会编译;我已经使用了 Foo<int> Foo<T>.Bar(T) 是指各自的类型和方法信息对象,这些对象很容易获得,但会使示例过于复杂)。

    坏消息是,只有当泛型类型定义是一个类型生成器时,这才有效,也就是说,当您发出一个泛型类型时。

    解决方案2:metadatatoken。 在从泛型类型定义到泛型构造类型的转换中,类型成员保留其metadatatoken是一个鲜为人知的事实。所以在你的例子中, foo<t>.bar(t)条 foo<int>.bar(t-as-int) 应该共享相同的metadatatoken。这样就可以做到:

    var barWithGenericParameterInfo = typeof(Foo<>).GetMethods()
       .Where(mi => mi.Name == "Bar" && 
              mi.GetParameters()[0].ParameterType.IsGenericParameter);
    
    var mappedBarInfo = foo.GetType().GetMethods()
        .Where(mi => mi.MetadataToken == genericBarInfo.MetadataToken);
    

    (这也不会编译,除非我非常幸运并且第一次成功地编译了它:)

    这个解决方案的问题是,metadatatoken并不是为了这个(很可能,文档在这个方面有点肤浅),它感觉像一个肮脏的黑客。尽管如此,它还是有效的。

        2
  •  1
  •   adrianbanks    16 年前

    使用foo<int>时,bar(t)方法被类型化为bar(int),它与定义了int作为参数的方法没有区别。

    要获得bar(t)的正确方法定义,可以使用typeof(foo<>)而不是typeof(foo<int>)。

    这将使你能够分辨出两者的区别。尝试以下代码:

    
        public static void CallBar<TR>(Foo<TR> foo)
        {
            Func<MethodInfo, bool> match = m => m.Name == "Bar";
    
            Type fooType = typeof(Foo<>);
            Console.WriteLine("{0}:", fooType);
            MethodInfo[] methods = fooType.GetMethods().Where(match).ToArray();
    
            foreach (MethodInfo mi in methods)
            {
                Console.WriteLine(mi);
            }
    
            Console.WriteLine();
    
            fooType = foo.GetType();
            Console.WriteLine("{0}:", fooType);
            methods = fooType.GetMethods().Where(match).ToArray();
    
            foreach (MethodInfo mi in methods)
            {
                Console.WriteLine(mi);
            }
        }
    

    这将输出:

    系统字符串条(T)
    系统字符串栏(Int32)

    系统字符串栏(Int32)
    系统字符串栏(Int32)

        3
  •  1
  •   johnnycrash    16 年前

    尝试查看泛型类型定义:typeof(foo<>)。这些方法的顺序相同。

      public class Foo<T> {
        public string Bar(T value) { return "Called Bar(T)"; }
        public string Bar(int value) { return "Called Bar(int)"; }
        public static void CallBar<TR>(Foo<TR> foo) {
    
          var footinfo = typeof(Foo<>).GetMethods();
          int i;
          for (i = 0; i < footinfo.Count(); ++i) {
            if (footinfo[i].Name == "Bar" && footinfo[i].GetParameters()[0].ParameterType.IsGenericParameter == false)
              break;
          }
    
          Console.WriteLine(foo.GetType().GetMethods()[i].Invoke(foo, new object[] { 0 }));
        }
      }
      // prints Bar(int)...
      Foo<int>.CallBar( new Foo<int>() );
    

    containsGenericParameters属性对于foo中的两个bar为true,对于foo中的两个bar为false,因此它是无用的。

        5
  •  0
  •   Paul Suart Wes    16 年前

    正如EricLippert指出的,它们都不是通用方法;您的 是泛型的,但您传递的是类的非泛型实例。因此,这些方法不是反射看到它的一般方法。

    如果你换衣服的话,你应该走在正确的轨道上

    foo.GetType()
    

    foo.GetGenericTypeDefinition()
    

    有关详细信息,请参阅 MSDN's documentation .

    推荐文章