I am trying this simple example on creating a generic method using Reflection.Emit but it is causing an exception when calling Invoke and I can't find the problem.
我正在尝试使用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 });
}
}
When calling genMeth.Invoke, the following exception is thrown
调用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
1 个解决方案
#1
3
Your method is static and has two arguments: argument 0 and argument 1. But in the generated IL code you're accessing an argument 2 which does not exist.
您的方法是静态的并且有两个参数:参数0和参数1.但是在生成的IL代码中,您正在访问不存在的参数2。
Another problem is that you're trying to pass an Int32 value to a method that expects a String reference. Box both arguments and pass them to the WriteLine overload that accepts an Object reference.
另一个问题是您尝试将Int32值传递给需要String引用的方法。装入两个参数并将它们传递给接受Object引用的WriteLine重载。
You also have declared your method to return an Int32, but you're not putting anything on the stack before returning.
您还声明了返回Int32的方法,但在返回之前,您没有在堆栈上放置任何内容。
Try to write the method in C# and then take a look at what IL the C# compiler emits (for example, using ILspy). It should look like this:
尝试在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
#1
3
Your method is static and has two arguments: argument 0 and argument 1. But in the generated IL code you're accessing an argument 2 which does not exist.
您的方法是静态的并且有两个参数:参数0和参数1.但是在生成的IL代码中,您正在访问不存在的参数2。
Another problem is that you're trying to pass an Int32 value to a method that expects a String reference. Box both arguments and pass them to the WriteLine overload that accepts an Object reference.
另一个问题是您尝试将Int32值传递给需要String引用的方法。装入两个参数并将它们传递给接受Object引用的WriteLine重载。
You also have declared your method to return an Int32, but you're not putting anything on the stack before returning.
您还声明了返回Int32的方法,但在返回之前,您没有在堆栈上放置任何内容。
Try to write the method in C# and then take a look at what IL the C# compiler emits (for example, using ILspy). It should look like this:
尝试在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