给定一个标识非泛型类的开放泛型方法的 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
Method: System.Object Method[Int32]()
在 .Net 4.0 上,它将是:
Method: System.Object Method[T]()
因此,在 .Net 2.0/3.5 中,为 ldtoken 生成的 IL 将包含一个元数据标记,该标记标识使用 GeneratedMethod
但是,在 .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/