我在使用反射来区分泛型类上的非泛型方法和泛型方法时遇到了一些麻烦。这是我正在使用的测试用例:
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>() );
最佳答案
不幸的是,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)
来表示各自的 Type 和 MethodInfo 对象,它们很容易获得,但会使示例过于复杂)。
坏消息是,这仅在通用类型定义为 TypeBuilder 时有效,即当您发出通用类型时。
解决方案#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 并非用于此目的(可能;文档对此有点吝啬),感觉像是一个肮脏的 hack。尽管如此,它还是有效的。
关于c# - 使用反射区分重载方法的泛型和非泛型版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/984034/