在 Microsoft IL 中,要调用值类型的方法,您需要间接引用。假设我们有一个名为“il”的 ILGenerator,并且当前我们在堆栈顶部有一个 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));
因此,您不能简单地使用堆栈上的值调用方法,因为它是值类型(尽管如果它是引用类型,则可以)。
我想要实现(或知道是否可能)是替换显示注释掉的三行,但保持程序正常工作,而不使用临时本地。