为什么存储局部变量并读回它会触发 TargetInvocationException?
Why does storing a local variable and reading it back trigger a TargetInvocationException?
假设我有这个方法:
MethodBuilder doubleMethod = typeBuilder.DefineMethod("Double",
MethodAttributes.Public | MethodAttributes.Static,
typeof(int), new [] { typeof(int) });
ILGenerator il = countMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // We load the input argument (an int) into the evaluation stack
il.Emit(OpCodes.Ldc_I4_2); // We load the integer 2 into the evaluation stack
il.Emit(OpCodes.Mul); // We multiply both numbers (n * 2)
il.Emit(OpCodes.Ret); // We return what is left on the evaluation stack, i.e. the result of the multiplication
我可以成功调用这个方法:
Type type = typeBuilder.CreateType();
MethodInfo method = type.GetMethod("Double");
object result = method.Invoke(null, new object[] { 4 }); // result = 8
但是,如果我将我的 IL 代码更改为:
il.Emit(OpCodes.Ldarg_0); // We load the input argument (an int) into the evaluation stack
il.Emit(OpCodes.Ldc_I4_2); // We load the integer 2 into the evaluation stack
il.Emit(OpCodes.Mul); // We multiply both numbers (n * 2)
/* I added these two instructions */
il.Emit(OpCodes.Stloc_0); // We pop the value from the evaluation stack and store into a local variable
il.Emit(OpCodes.Ldloc_0); // We read that local variable and push it back into the evaluation stack
il.Emit(OpCodes.Ret); // We return what is left on the evaluation stack, i.e. the result of the multiplication
尝试调用生成的方法时,抛出以下异常:
TargetInvocationException was unhandled - Exception has been thrown by the target of an invocation.
这是为什么?我的意思是,从评估堆栈中弹出值然后再次压入相同的值应该绝对没有任何作用。当它到达 OpCodes.Ret
时,正确的值应该在计算堆栈上。
要在 IL 中使用局部变量,您首先需要声明它,以便运行时知道它的类型。为此,请使用 ILGenerator.DeclareLocal()
.
在发出使用该变量的指令时,您也可以考虑 using the LocalBuilder
returned from DeclareLocal()
。这样,您就不需要记住所有局部变量的索引:
var local = il.DeclareLocal(typeof(int));
…
il.Emit(OpCodes.Stloc, local);
il.Emit(OpCodes.Ldloc, local);
假设我有这个方法:
MethodBuilder doubleMethod = typeBuilder.DefineMethod("Double",
MethodAttributes.Public | MethodAttributes.Static,
typeof(int), new [] { typeof(int) });
ILGenerator il = countMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // We load the input argument (an int) into the evaluation stack
il.Emit(OpCodes.Ldc_I4_2); // We load the integer 2 into the evaluation stack
il.Emit(OpCodes.Mul); // We multiply both numbers (n * 2)
il.Emit(OpCodes.Ret); // We return what is left on the evaluation stack, i.e. the result of the multiplication
我可以成功调用这个方法:
Type type = typeBuilder.CreateType();
MethodInfo method = type.GetMethod("Double");
object result = method.Invoke(null, new object[] { 4 }); // result = 8
但是,如果我将我的 IL 代码更改为:
il.Emit(OpCodes.Ldarg_0); // We load the input argument (an int) into the evaluation stack
il.Emit(OpCodes.Ldc_I4_2); // We load the integer 2 into the evaluation stack
il.Emit(OpCodes.Mul); // We multiply both numbers (n * 2)
/* I added these two instructions */
il.Emit(OpCodes.Stloc_0); // We pop the value from the evaluation stack and store into a local variable
il.Emit(OpCodes.Ldloc_0); // We read that local variable and push it back into the evaluation stack
il.Emit(OpCodes.Ret); // We return what is left on the evaluation stack, i.e. the result of the multiplication
尝试调用生成的方法时,抛出以下异常:
TargetInvocationException was unhandled - Exception has been thrown by the target of an invocation.
这是为什么?我的意思是,从评估堆栈中弹出值然后再次压入相同的值应该绝对没有任何作用。当它到达 OpCodes.Ret
时,正确的值应该在计算堆栈上。
要在 IL 中使用局部变量,您首先需要声明它,以便运行时知道它的类型。为此,请使用 ILGenerator.DeclareLocal()
.
在发出使用该变量的指令时,您也可以考虑 using the LocalBuilder
returned from DeclareLocal()
。这样,您就不需要记住所有局部变量的索引:
var local = il.DeclareLocal(typeof(int));
…
il.Emit(OpCodes.Stloc, local);
il.Emit(OpCodes.Ldloc, local);