【问题标题】:Why does storing a local variable and reading it back trigger a TargetInvocationException?为什么存储局部变量并读回它会触发 TargetInvocationException?
【发布时间】:2015-11-03 16:29:16
【问题描述】:

假设我有这个方法:

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 未处理 - 调用目标已抛出异常。

这是为什么?我的意思是,从评估堆栈中弹出值然后再次推送相同的值应该什么都不做。当它到达OpCodes.Ret 时,正确的值应该在评估堆栈上。

【问题讨论】:

  • 在您对此感到热血沸腾之前,首先要了解如何诊断 TargetInvocationException。您必须查看 InnerException 以找出问题所在。并使用 ILGenerator.DeclareLocal() 以便它可以正确测量所需的堆栈大小。
  • @HansPassant 实际上,内部异常只是声明"Common Language Runtime detected an invalid program",我认为它太笼统而无法找到相关性,堆栈跟踪似乎也没有帮助。
  • @HansPassant 添加il.DeclareLocal(typeof(int)) 成功了!谢谢!您可以将其发布为答案吗? (另外,为什么我需要调用这个方法?)

标签: c# .net reflection cil reflection.emit


【解决方案1】:

要在 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-18
    • 2010-09-12
    • 2013-07-05
    • 2019-01-24
    • 1970-01-01
    • 2015-06-23
    • 1970-01-01
    • 2019-03-05
    相关资源
    最近更新 更多