C# ILGenerator在int上发出GetHashCode调用

C# ILGenerator在int上发出GetHashCode调用,c#,.net-core,C#,.net Core,当我运行此代码时: var il = getHashCode.GetILGenerator(); il.Emit(OpCodes.Ldc_I4_S, 17); // put "17" on the stack il.Emit(OpCodes.Call, typeof(Int32).GetMethod("GetHashCode", new Type[] { })); il.Emit(OpCodes.Ret); 我正在获取System.NullReferenceException:对象引用未设置

当我运行此代码时:

var il = getHashCode.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_S, 17); // put "17" on the stack
il.Emit(OpCodes.Call, typeof(Int32).GetMethod("GetHashCode", new Type[] { })); 
il.Emit(OpCodes.Ret);
我正在获取System.NullReferenceException:对象引用未设置为对象的实例

当我框选值时:

var il = getHashCode.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_S, 17); // put "17" on the stack
il.Emit(OpCodes.Box, typeof(Int32));
il.Emit(OpCodes.Call, typeof(Int32).GetMethod("GetHashCode", new Type[] { })); 
il.Emit(OpCodes.Ret);
返回值为-1875039000,但应为17

如何发出正确的调用?

GetHashCode()是一个实例方法,因此需要在“引用”上调用它。虽然您不需要框装整数,但需要推送到堆栈上的
这个
参数不是整数值本身,而是指向整数值的指针

因此,您需要一个局部变量,可以在其中存储整数值,然后将指向该局部值的指针推送到堆栈上(
ldloca.s
),并调用实例方法:

static void Main(string[] args)
{
    var method = new DynamicMethod("Get17HashCode", typeof(int), new Type[0], typeof(Program).Module);
    var ilGenerator = method.GetILGenerator();
    ilGenerator.DeclareLocal(typeof(int));
    ilGenerator.Emit(OpCodes.Ldc_I4_S, 17);
    ilGenerator.Emit(OpCodes.Stloc_0);
    ilGenerator.Emit(OpCodes.Ldloca_S, 0);
    ilGenerator.Emit(OpCodes.Call, typeof(int).GetMethod(nameof(int.GetHashCode)));
    ilGenerator.Emit(OpCodes.Ret);

    var delegateFunction = (Func<int>)method.CreateDelegate(typeof(Func<int>));

    var result = delegateFunction();
    Console.WriteLine($"Got {result}");
}
static void Main(字符串[]args)
{
var方法=新的DynamicMethod(“Get17HashCode”,typeof(int),new Type[0],typeof(Program).Module);
var ilGenerator=method.GetILGenerator();
ilGenerator.DeclareLocal(type of(int));
ilGenerator.Emit(操作码.Ldc_I4_S,17);
ilGenerator.Emit(操作码.Stloc_0);
ilGenerator.Emit(操作码.Ldloca_S,0);
Emit(opcode.Call,typeof(int).GetMethod(nameof(int.GetHashCode));
ilGenerator.Emit(操作码.Ret);
var delegateFunction=(Func)method.CreateDelegate(typeof(Func));
var result=delegateFunction();
WriteLine($“Got{result}”);
}

您得到
NullReferenceException
的原因可能是因为地址
17
仍在CLR为其注册空引用处理程序的地址
0
处注册的虚拟内存页中。较大的值应导致
AccessViolationException:尝试读取或写入受保护的内存
(假设堆栈上有一个简短的表示)。

先用C#编写代码,然后使用像ildasm.exe这样的反编译器查看它使用的操作码。它的工作方式与您想象的不同,简单的值类型经过了大量优化以避免装箱。您需要存储在本地,然后从该本地重新加载。某种形式的stloc/ldloc。试试看sharplab@pinkfloydx33需要本地,以便将指向该本地的地址作为指针推送到堆栈上(
ldloca.s
),不是值本身。为什么在已装箱的实例上调用该方法不起作用?那么您需要对
对象的
GetHashCode
执行虚拟方法调用:
ilGenerator.Emit(opcode.Callvirt,typeof(object.GetHashCode))