2015-11-03 51 views
3

让我们假设我有这样的方法:为什么存储一个局部变量并将其读回触发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未处理 - 调用的目标引发了异常。

这是为什么?我的意思是,从评估堆栈中弹出值,然后再次推入相同的值应该绝对不要做任何事情。到达OpCodes.Ret时,正确的值应该在评估堆栈上。

+2

在您变得非常热并且沉溺于此之前,请先了解如何诊断TargetInvocationException。你*有*看看InnerException来找出哪里出了问题。并使用ILGenerator.DeclareLocal(),以便它可以正确测量所需的堆栈大小。 –

+0

@HansPassant实际上,内部异常只是声明'“公共语言运行时检测到一个无效的程序”,我认为它太泛化了,以至于找不到相关的东西,堆栈跟踪也没有任何帮助。 –

+0

@HansPassant添加'il.DeclareLocal(typeof(int))'诀窍!谢谢!你可以张贴它作为一个水管? (另外,为什么我需要调用此方法?) –

回答

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); 
相关问题