2013-01-07 168 views
9

我有一些代碼使用在生成類型上找到的泛型方法的MethodInfo。爲了避免一些反思,我的代碼使用了爲什麼TypeBuilder生成的泛型methodinfo不是泛型方法?

ldtoken Method 
ldtoken Type 
call GetMethodFromHandle(RuntimeMethodHandle,RunTimeTypeHandle) 

在編譯時生成MethodInfos的模式。

但是,如果methodInfo屬於一個泛型類型,並且它本身是一個泛型方法,那麼事情會變得棘手。 這是一些代碼,它只是簡單地生成一個GM,它會發出它的methodInfo的開放版本。 如果我把它稱爲檢索方法不是試圖通過特定類型我得到一個令人困惑的異常:

System.Reflection.MethodInfo GM[M]()關閉它是不是一個GenericMethodDefinition。 MakeGenericMethod只能在MethodBase.IsGenericMethodDefinition爲true的方法上調用。

下面是相關代碼::

var aBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.RunAndSave); 
var mBuilder = aBuilder.DefineDynamicModule(aBuilder.GetName().Name, true); 
var typeBuilder = mBuilder.DefineType("NameSpace.Generic`1",TypeAttributes.AutoClass | TypeAttributes.Sealed | TypeAttributes.Public,typeof(object)); 
var TypeGenerics = typeBuilder.DefineGenericParameters(new[] { "T" }); 
var methodBuilder = typeBuilder.DefineMethod("GM", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig); 
var methodGenerics = methodBuilder.DefineGenericParameters(new[] { "M" }); 
methodBuilder.SetSignature(typeof(MethodInfo), null, null, Type.EmptyTypes, null, null); 
var ilgenerator = methodBuilder.GetILGenerator(); 
var typeBuilderClosedOverT = typeBuilder.MakeGenericType(TypeGenerics); 
ilgenerator.Emit(OpCodes.Ldtoken, methodBuilder); 
ilgenerator.Emit(OpCodes.Ldtoken, typeBuilderClosedOverT); 
ilgenerator.Emit(OpCodes.Call, 
    typeof(MethodBase).GetMethod(
     "GetMethodFromHandle", 
     BindingFlags.Public | BindingFlags.Static, 
     null, 
     new[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) }, 
     null 
    ) 
); 
ilgenerator.Emit(OpCodes.Castclass,typeof(MethodInfo)); 
ilgenerator.Emit(OpCodes.Ret); 
var bakedType = typeBuilder.CreateType(); 
var methodInfo = bakedType.MakeGenericType(typeof(int)).GetMethod("GM").MakeGenericMethod(typeof(bool)).Invoke(null, null) as MethodInfo; 
var methodInfoClosedOverBool = methodInfo.MakeGenericMethod(typeof(bool)); 

看來只是時間我的代碼砸了是,如果它是一個非泛型類型genericmethod。如果代碼被重寫,以至於它關於正常類型的普通方法,或普通類型的普通方法,或者泛型類型的普通方法,它都可以工作。只有兩者的組合纔會導致錯誤。難道我做錯了什麼?

我提交了關於這個問題的錯誤: https://connect.microsoft.com/VisualStudio/feedback/details/775989/clr-cannot-emit-a-token-for-an-open-generic-method-on-a-generic-type

+1

有趣的是,雖然'methodInfo.GetGenericArguments()'返回一個泛型參數類型,它看起來像'methodInfo.IsGenericMethodDefinition'確實是'false'。 – svick

+0

是的,檢查它肯定使它看起來像該死的東西是通用的。我目前的解決方案是確保我的方法具有唯一的名稱並傳入'Type'和'string',並調用'type.GetMethod(...)',但我想避免反射。 –

+0

似乎現在解決了.net 4.7 –

回答

3

看起來像一個CLR問題給我,因爲如果你手工編寫的IL,並使用ILASM同樣的事情發生。也就是說,給定泛型類G和非泛型類N,每一個泛型方法M,然後試圖從非泛型類獲取泛型方法定義的工作原理:

ldtoken method void class N::M<[1]>() 
ldtoken class N<!T> 
call  class [mscorlib]System.Reflection.MethodBase [mscorlib] 
      System.Reflection.MethodBase::GetMethodFromHandle(
       valuetype [mscorlib]System.RuntimeMethodHandle, 
       valuetype [mscorlib]System.RuntimeTypeHandle) 
castclass [mscorlib]System.Reflection.MethodInfo 
ret 

MethodInfo從返回通用類是不是泛型方法定義(但它幾乎是,它是D.MakeGenericMethod(D.GetGenericArguments())其中D是你想要的方法定義):

ldtoken method void class G`1<!T>::M<[1]>() 
ldtoken class G`1<!T> 
call  class [mscorlib]System.Reflection.MethodBase [mscorlib] 
      System.Reflection.MethodBase::GetMethodFromHandle(
       valuetype [mscorlib]System.RuntimeMethodHandle, 
       valuetype [mscorlib]System.RuntimeTypeHandle) 
castclass [mscorlib]System.Reflection.MethodInfo 
ret 
+0

嗯,謝謝我實際上只是做具體類型(列表 .ConvertAll)同樣的事情,我要報告這是一個錯誤的微軟。我懷疑會發生什麼:( –

+0

有趣的是,這個IL不能被編譯回來:「error:token at error'['' – IllidanS4

0

問題在於,因爲,由於在ldtoken method指令內IL無法表達通用方法定義,CLR會加載錯誤的方法。該指令被反編譯ILDASM此:

ldtoken method class [mscorlib]System.Reflection.MethodInfo class NameSpace.Generic`1<!T>::GM<[1]>() 

這甚至不是有效的IL。然後,CLR混淆指令,並從它自己的泛型參數中加載一個泛型方法實例。

var methodInfoClosedOverBool = (methodInfo.IsGenericMethodDefinition ? methodInfo : methodInfo.GetGenericMethodDefinition()).MakeGenericMethod(typeof(bool)); 

更多的測試,我做了展示同樣的問題更短的代碼:

DynamicMethod dyn = new DynamicMethod("", typeof(RuntimeMethodHandle), null); 
var il = dyn.GetILGenerator(); 
il.Emit(OpCodes.Ldtoken, typeof(GenClass<string>).GetMethod("GenMethod")); 
il.Emit(OpCodes.Ret); 
var handle = (RuntimeMethodHandle)dyn.Invoke(null, null); 
var m = MethodBase.GetMethodFromHandle(handle, typeof(GenClass<int>).TypeHandle); 

GetMethodFromHandle(這也應該只需要方法處理,不聲明類型)僅設置聲明鍵入(通知<int><string>無所謂),並且不會出錯。

相關問題