.net - 更改了 .Net 4 中泛型方法的 LdToken 行为?

标签 .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()”:
IL.Emit(OpCodes.Ldtoken, methodInfo);
IL.Emit(OpCodes.Call, methodInfoPrintMethodInfo);

调用 GeneratedMethod(),.Net 3.5 上的输出将是:
Method: System.Object Method[Int32]()

在 .Net 4.0 上,它将是:
Method: System.Object Method[T]()

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

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

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

最佳答案

反汇编生成的代码时,可以看到在 .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属于GeneratedMethod<T> . (实际上,这与为 IL.Emit (OpCodes.Ldtoken, methodInfo.MakeGenericMethod (<typeParameterOfGeneratedMethod>)) 发出的 IL 相同。)这个 !!0即使在 GeneratedMethod 时也会发出引用根本不是通用的 - 生成的程序不再可验证(并且在执行时会导致 BadImageFormatException)。

这显然是一个错误,在 .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 没有发出引用以正确打开泛型方法。我怀疑它也连接到 the fact that IL assembler didn't even have a syntax to denote open generic methods in the past .

关于.net - 更改了 .Net 4 中泛型方法的 LdToken 行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12232561/

相关文章:

c# - 所有任务完成后继续任务

c# - 如何在C#中使用泛型执行此操作

c# - 添加 dll 引用到 asp.net mvc 应用程序,无需重新启动应用程序

reflection - 通过混合列出类的方法

C# 将对象转换为具有 Type 参数的通用接口(interface)

c# - 使用反射获取 INotifyPropertyChanged 通知的属性的实际值?

c# - 无法将字符串转换为字符

c# - .NET 中的 RSA 加密 - JAVA 中的解密 -> Java 抛出 "modulus not positive"错误

c# - 如何使用带有 CancellationToken 的任务包装单个同步操作?

c# - 使用 C# 运行 shell 命令并将信息获取到字符串中