改变了.Net 4中泛型方法的LdToken行为?

时间:2012-09-02 00:43:23

标签: .net reflection .net-4.0 reflection.emit

给定一个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的行为似乎更合乎逻辑。我也找不到任何关于这种变化的文件。这是现在修复的早期版本中的错误吗?

1 个答案:

答案 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有关。