2015-11-03 2 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 На самом деле, внутреннее исключение просто указывает, что «Common Language Runtime обнаружил недопустимую программу», я думал, что это слишком общее, чтобы найти его релевантным, и трассировка стека тоже не поможет. –

+0

@HansPassant Добавление 'il.DeclareLocal (typeof (int))' сделал трюк! Спасибо! Можете ли вы опубликовать его как anwer? (Также, почему мне нужно вызвать этот метод?) –

ответ

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); 
Смежные вопросы