В Microsoft IL для вызова метода с типом значения необходима косвенная ссылка. Допустим, у нас есть ILGenerator с именем «il» и что в настоящее время у нас есть Nullable на вершине стека, если мы хотим проверить, имеет ли он значение, мы могли бы выдать следующее:
var local = il.DeclareLocal(typeof(Nullable<int>));
il.Emit(OpCodes.Stloc, local);
il.Emit(OpCodes.Ldloca, local);
var method = typeof(Nullable<int>).GetMethod("get_HasValue");
il.EmitCall(OpCodes.Call, method, null);
Однако было бы неплохо пропустить сохранение в качестве локальной переменной и просто вызвать метод по адресу переменной, уже находящейся в стеке, что-то вроде:
il.Emit(/* not sure */);
var method = typeof(Nullable<int>).GetMethod("get_HasValue");
il.EmitCall(OpCodes.Call, method, null);
Семейство инструкций ldind выглядит многообещающе (особенно ldind_ref), но я не могу найти достаточную документацию, чтобы знать, может ли это вызвать упаковку значения, что, как я подозреваю, может.
Я посмотрел на вывод компилятора C #, но он использует локальные переменные для достижения этого, что заставляет меня поверить, что первый способ может быть единственным. У кого-нибудь есть идеи получше?
**** Редактировать: Дополнительные примечания ****
Попытка прямого вызова метода, как в следующей программе с закомментированными строками, не работает (ошибка будет «Операция может дестабилизировать среду выполнения»). Раскомментируйте строки, и вы увидите, что все работает как положено, возвращая "True".
var m = new DynamicMethod("M", typeof(bool), Type.EmptyTypes);
var il = m.GetILGenerator();
var ctor = typeof(Nullable<int>).GetConstructor(new[] { typeof(int) });
il.Emit(OpCodes.Ldc_I4_6);
il.Emit(OpCodes.Newobj, ctor);
//var local = il.DeclareLocal(typeof(Nullable<int>));
//il.Emit(OpCodes.Stloc, local);
//il.Emit(OpCodes.Ldloca, local);
var getValue = typeof(Nullable<int>).GetMethod("get_HasValue");
il.Emit(OpCodes.Call, getValue);
il.Emit(OpCodes.Ret);
Console.WriteLine(m.Invoke(null, null));
Так что вы не можете просто вызвать метод со значением в стеке, потому что это тип значения (хотя вы могли бы, если бы это был ссылочный тип).
Чего я хотел бы добиться (или узнать, возможно ли это), так это заменить три строки, которые показаны закомментированными, но сохранить работоспособность программы без использования временного локального.