简体   繁体   English

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

[英]Differentiating between generic and non-generic version of overloaded method using reflection

I'm having some trouble using reflection to differentiate between a non-generic and a generic method on a generic class. 我在使用反射来区分泛型类的非泛型和泛型方法时遇到了一些麻烦。 Here's a test case I'm working with: 这是我正在使用的测试用例:

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

Unfortunately System.Reflection doesn't provide a good way to correlate a method on a constructed type with the corresponding method on the generic type definition from which it was constructed. 遗憾的是,System.Reflection没有提供一种很好的方法来将构造类型的方法与构造它的泛型类型定义上的相应方法相关联。 There are two solutions I know of, neither one is perfect: 我知道有两种解决方案,一种都不完美:

Solution #1: static TypeBuilder.GetMethod. 解决方案#1:静态TypeBuilder.GetMethod。 There's a static version of GetMethod on TypeBuilder that accepts a generic constructed type and a MethodInfo for a method on a generic type definition, and returns the corresponding method on the specified generic type. TypeBuilder上一个静态版本的GetMethod,它接受泛型类型定义的方法的泛型构造类型和MethodInfo,并返回指定泛型类型的相应方法。 In this example, calling TypeBuilder.GetMethod(Foo<int>, Foo<T>.Bar(T)) will give you Foo<int>.Bar(T-as-int) which you can then use to disambiguate between it and Foo<int>.Bar(int) . 在这个例子中,调用TypeBuilder.GetMethod(Foo<int>, Foo<T>.Bar(T))将为您提供Foo<int>.Bar(T-as-int) ,然后您可以使用它来消除它与它之间的歧义。 Foo<int>.Bar(int)

(The above example will not compile, naturally; I've used Foo<int> and Foo<T>.Bar(T) to mean the respective Type and MethodInfo objects which, which are easily obtainable but would make the example too complex). (上面的例子自然不会编译;我使用Foo<int>Foo<T>.Bar(T)来表示相应的Type和MethodInfo对象,这些对象很容易获得,但会使示例过于复杂) 。

The bad news is that this only works when the generic type definition is a TypeBuilder, ie when you're emitting a generic type. 坏消息是,只有在泛型类型定义是TypeBuilder时才会起作用,即当您发出泛型类型时。

Solution #2: MetadataToken. 解决方案#2:MetadataToken。 It's a little known fact that type members retain their MetadataToken in the transition from generic type definitions to generic constructed types. 一个鲜为人知的事实是类型成员在从泛型类型定义到泛型构造类型的过渡中保留其MetadataToken。 So in your example, Foo<T>.Bar(T) and Foo<int>.Bar(T-as-int) should share the same MetadataToken. 因此在您的示例中, Foo<T>.Bar(T)Foo<int>.Bar(T-as-int)应该共享相同的MetadataToken。 That would allow you to do this: 那将允许你这样做:

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

(This will not compile either, unless I'm extremely lucky and managed to get it right the first time :) ) (这也不会编译,除非我非常幸运并设法在第一次就把它弄好:))

The problem with this solution is that MetadataToken wasn't meant for that (probably; the documentation is a little skimpy on that) and it feels like a dirty hack. 这个解决方案的问题在于MetadataToken并不是为了那个(可能;文档有点吝啬)并且感觉就像一个肮脏的黑客。 Nevertheless, it works. 不过,它有效。

When using Foo<int>, the Bar(T) method is typed as Bar(int), making no distinction between it and the method with an int defined as the parameter. 使用Foo <int>时,Bar(T)方法的类型为Bar(int),它与定义为参数的int的方法没有区别。

To get the correct method definition of Bar(T), you can use typeof(Foo<>) instead of typeof(Foo<int>). 要获得Bar(T)的正确方法定义,可以使用typeof(Foo <>)而不是typeof(Foo <int>)。

This will enable you to tell the difference between the two. 这将使您能够分辨出两者之间的区别。 Try the following code: 请尝试以下代码:


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

This will output: 这将输出:

System.String Bar(T) System.String Bar(T)
System.String Bar(Int32) System.String Bar(Int32)

System.String Bar(Int32) System.String Bar(Int32)
System.String Bar(Int32) System.String Bar(Int32)

Try looking at the generic type definition: typeof(Foo<>). 尝试查看泛型类型定义:typeof(Foo <>)。 The methods will be in the same order. 方法将按相同的顺序排列。

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

The ContainsGenericParameters property is true for both Bar's in Foo<> and false for both Bar's in Foo, so its useless. ContainsGenericParameters属性对于Foo <>中的两个Bar都是true,对于Foo中的两个Bar都是false,所以它没用。

As Eric Lippert points out, neither of them are generic methods; 正如Eric Lippert指出的那样,它们都不是通用方法; your class is generic, but you're passing a non-generic instance of the class. 您的是通用的,但您传递的是该类的非泛型实例。 Therefore the methods aren't generic the way reflection sees it. 因此,这些方法不像反射那样通用。

You should be on the right track if you change 如果你改变,你应该走在正确的轨道上

foo.GetType()

to

foo.GetGenericTypeDefinition()

For more info, see MSDN's documentation . 有关详细信息,请参阅MSDN的文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM