4

给定一个识别非泛型类的开放泛型方法的 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<T>()”:

IL.Emit(OpCodes.Ldtoken, methodInfo);
IL.Emit(OpCodes.Call, methodInfoPrintMethodInfo);

调用 GeneratedMethod<int>(),.Net 3.5 上的输出将是:

Method: System.Object Method[Int32]()

在 .Net 4.0 上,它将是:

Method: System.Object Method[T]()

因此,在 .Net 2.0/3.5 中,为 ldtoken 生成的 IL 将包含一个元数据令牌,该元数据令牌标识通用 FooMethod<>,该通用 FooMethod<> 使用调用 GeneratedMethod<T> 时给出的类型参数进行实例化。

然而,在 .Net 4.0 中,ldtoken 将包含标识开放泛型类型的元数据。

我很难找到支持 .Net 3.5 案例中发生的事情的文档(实际上,如果生成的方法本身不是通用的,它应该完全失败) - .Net 4 的行为似乎更合乎逻辑。我也找不到任何有关更改的文档。这是现在已经修复的早期版本中的错误吗?

4

1 回答 1

5

当您反汇编生成的代码时,您可以看到在 .NET 3.5ldtoken中,与 open 泛型方法相关的指令发出如下:

.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实例化的。(事实上​​,这与为 . 发出的 IL 相同。)即使根本不是通用的,也会发出此引用- 结果程序不再可验证(并且在执行时会导致 BadImageFormatException)。TGeneratedMethod<T>IL.Emit (OpCodes.Ldtoken, methodInfo.MakeGenericMethod (<typeParameterOfGeneratedMethod>))!!0GeneratedMethod

这显然是一个错误,在 .NET 4 中,这似乎已修复,因为(反汇编)发出的代码现在看起来像这样:

.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

如您所见,签名现在指的是未实例化的FooMethod(在 IL 汇编中,这表示为FooMethod[1])。

所以是的,这看起来像是 .NET 3.5 中的一个错误,已在 .NET 4 中修复。但是,它似乎并没有改变 ; 的语义ldtoken。只是 Reflection.Emit 没有发出正确打开泛型方法的引用。我怀疑这也与IL 汇编器过去甚至没有表示开放泛型方法的语法有关

于 2012-10-15T09:43:15.980 回答