2013-07-23 49 views
0

我正在尝试使用Reflection.Emit创建泛型方法的这个简单示例,但调用Invoke时发生异常,而且找不到问题。使用反射发射泛型方法在调用时引发异常

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     AppDomain appDomain = AppDomain.CurrentDomain; 
     AssemblyName name = new AssemblyName("MyAssembly") { Version = new Version("1.0.0.0") }; 
     AssemblyBuilder ab = appDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave); 
     ModuleBuilder mb = ab.DefineDynamicModule("MyModule", "MyAssembly.dll"); 
     TypeBuilder tb = mb.DefineType("Widget", TypeAttributes.Public); 

     // Define a method builder 
     MethodBuilder methodBuilder = tb.DefineMethod("MyGenericMethod", MethodAttributes.Public | MethodAttributes.Static); 

     // Get generic type parameter builders 
     GenericTypeParameterBuilder[] genericParams = 
      methodBuilder.DefineGenericParameters("TKey", "TValue"); 

     methodBuilder.SetSignature(typeof(int), null, null, 
      genericParams, 
      null, null); 
     methodBuilder.DefineParameter(1, ParameterAttributes.None, "key"); 
     methodBuilder.DefineParameter(2, ParameterAttributes.None, "val"); 

     ILGenerator gen = methodBuilder.GetILGenerator(); 
     MethodInfo writeLineStr = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }); 
     gen.Emit(OpCodes.Ldarg_0); 
     gen.Emit(OpCodes.Box, genericParams[0]); 
     gen.Emit(OpCodes.Call, writeLineStr); 
     gen.Emit(OpCodes.Ldarg_1); 
     gen.Emit(OpCodes.Box, genericParams[1]); 
     gen.Emit(OpCodes.Call, writeLineStr); 
     gen.Emit(OpCodes.Ret); 

     Type t = tb.CreateType(); 
     MethodInfo genMeth = t.GetMethod("MyGenericMethod").MakeGenericMethod(typeof(string), typeof(int)); 
     genMeth.Invoke(null, new object[] { "NumberKey", 100 }); 
    } 
} 

当调用genMeth.Invoke,下面的异常被抛出

Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the t 
arget of an invocation. ---> System.InvalidProgramException: Common Language Runtime detected an inv 
alid program. 
    at Widget.MyGenericMethod[TKey,TValue](TKey key, TValue val) 
    --- End of inner exception stack trace --- 
    at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Bool 
ean constructor) 
    at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Obje 
ct[] arguments) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, 
Object[] parameters, CultureInfo culture) 
    at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) 
    at ReflectionEmitDemo14.Program.Main(String[] args) in d:\Projects\ReflectionEmitDemo\ReflectionE 
mitDemo14\Program.cs:line 50 

回答

3

你的方法是静态的,有两个参数:参数0和参数1但是,在生成的IL代码你所访问一个不存在的论据2。

另一个问题是,您试图将Int32值传递给期望String引用的方法。将两个参数放在一起并将它们传递给接受Object引用的WriteLine重载。

你也已经声明你的方法返回一个Int32,但是你没有在返回之前把任何东西放到栈上。

尝试在C#中编写方法,然后查看C#编译器发出的IL(例如,使用ILspy)。它应该看起来像这样:

ldarg.0 
box !TKey 
call void [mscorlib]System.Console::WriteLine(object) 

ldarg.1 
box !TValue 
call void [mscorlib]System.Console::WriteLine(object) 

ldc.i4.s 42 
ret 
+0

在实例方法中,Ldarg_0属于“this”,而Ldarg_1属于第一个参数。这是否意味着在静态方法Ldarg_0属于第一个参数? – randacun

+0

是的,静态方法没有“this”参数。 – dtb

+0

根据您的评论修改了代码,但仍显示异常。 – randacun