2012-09-02 41 views
4

鑑於識別非通用類的開放通用方法的MethodInfo實例,考慮下面的僞代碼:更改.Net 4中泛型方法的LdToken的行爲?

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"); 

生成方法「空隙GeneratedMethod <Ť>()」含有在體內驗證碼

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 <元數據標記>使用實例化的在調用GeneratedMethod <T>時給出的類型參數。

但是,在.Net 4.0中,ldtoken將包含標識開放泛型類型的元數據。

我很難找到支持.Net 3.5情況下發生的事情的文檔(事實上,如果生成的方法本身不是通用的,它應該完全失敗).Net 4行爲似乎更符合邏輯。我找不到任何有關更改的文檔。這是現在已經修復的早期版本中的錯誤嗎?

+0

你是什麼意思,「GeneratedMethod被調用時給出的類型參數」?你是否說你正在生成的'GeneratedMethod'是泛型的,並且你用'int'作爲類型參數來調用它? – svick

+0

@svick是的,這是正確的。我編輯過,以便格式化程序不會吞下通用參數。 –

回答

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爲IL.Emit (OpCodes.Ldtoken, methodInfo.MakeGenericMethod (<typeParameterOfGeneratedMethod>))所發出的IL相同。)即使GeneratedMethod根本不是通用的,也會發出!!0引用 - 所生成的程序將不再可驗證(並且在執行時會導致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中的一個bug,它已經被.NET 4修復了。但是,它似乎並沒有改變ldtoken的語義;只是Reflection.Emit沒有正確地引用正確打開的泛型方法。我懷疑它也連接到the fact that IL assembler didn't even have a syntax to denote open generic methods in the past

+0

感謝您確認我不是唯一一位在此處遇到問題的人。幸運的是,就我而言,事實證明,應用程序代碼在使用Reflection.Emit()時實際上是作弊,並且使其更嚴格,使得代碼在3.5和4.0上的工作都相同。 –