给定一个MethodInfo实例来标识非泛型类的开放泛型方法,请考虑以下伪代码:
class Foo { void FooMethod<T>() {} }
public static void PrintMethodInfo(RuntimeMethodHandle methodHandle)
{
var mi = (MethodInfo) MethodBase.GetMethodFromHandle(methodHandle);
Console.WriteLine("Method: "+mi.ToString());
}
var methodInfo = typeof(Foo).GetMethod("FooMethod");
生成一个方法“void GeneratedMethod&lt; T&gt;()”在正文中包含此代码:
IL.Emit(OpCodes.Ldtoken, methodInfo);
IL.Emit(OpCodes.Call, methodInfoPrintMethodInfo);
调用GeneratedMethod&lt; int&gt;(),.Net 3.5上的输出将是:
Method: System.Object Method[Int32]()
在.Net 4.0上,它将是:
Method: System.Object Method[T]()
因此,似乎在.Net 2.0 / 3.5中,为ldtoken生成的IL将包含标识通用FooMethod&lt;&gt;的元数据标记。使用GeneratedMethod&lt; T&gt;时给出的类型参数进行实例化。被叫了。
在.Net 4.0中,ldtoken将包含标识开放泛型类型的元数据。
我很难找到支持.Net 3.5案例中发生的事情的文档(事实上,如果生成的方法本身不是通用的,它应该完全失败) - .Net 4的行为似乎更合乎逻辑。我也找不到任何关于这种变化的文件。这是现在修复的早期版本中的错误吗?
答案 0 :(得分:5)
当您反汇编生成的代码时,您可以看到在.NET 3.5中,与开放泛型方法相关的ldtoken
指令发出如下:
.method public static void GeneratedMethod<T>() cil managed
{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldtoken method instance void [ConsoleApplication16]ConsoleApplication16.Program/Foo::FooMethod<!!0>()
IL_0005: call void [ConsoleApplication16]ConsoleApplication16.Program::PrintMethodInfo(valuetype [mscorlib]System.RuntimeMethodHandle)
IL_000a: ret
} // end of method TestType::GeneratedMethod
语法!!0
是对周围方法(GeneratedMethod
)的类型参数的引用,因此Foo
方法加载实例化,T
属于{ {1}}。 (实际上,这与为GeneratedMethod<T>
发出的IL相同。)即使IL.Emit (OpCodes.Ldtoken, methodInfo.MakeGenericMethod (<typeParameterOfGeneratedMethod>))
根本不是通用的,也会发出此!!0
引用 - 结果程序不再是可验证的(并且,在执行时,会导致BadImageFormatException)。
这显然是一个错误,在.NET 4中,这似乎是固定的,因为(反汇编)发出的代码现在看起来像这样:
GeneratedMethod
如您所见,签名现在引用未实例化的.method public static void GeneratedMethod<T>() cil managed
{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldtoken method instance void [ConsoleApplication16]ConsoleApplication16.Program/Foo::FooMethod<[1]>()
IL_0005: call void [ConsoleApplication16]ConsoleApplication16.Program::PrintMethodInfo(valuetype [mscorlib]System.RuntimeMethodHandle)
IL_000a: ret
} // end of method TestType::GeneratedMethod
(在IL程序集中,这表示为FooMethod
)。
所以是的,这看起来像.NET 3.5中用.NET 4修复的错误。但是,它似乎不是FooMethod[1]
语义的变化。只是Reflection.Emit没有正确地发出对开放泛型方法的引用。我怀疑它也与the fact that IL assembler didn't even have a syntax to denote open generic methods in the past有关。