为什么为指针和 ref 生成的 IL 代码相同但签名不同?

Why the generated IL code for a pointer and ref is same but signatures are different?

我正在尝试找出如何在 C# 中重现 ref 修饰符的行为。

我有两个方法:

static void Foo(ref int x)
{
     x = 25;
}

unsafe static void Foo(int* x)
{
    *x = 25;
}

尽管这两种方法生成的IL代码相同,但签名不同:

Foo(ref int)的代码:

.method private hidebysig static void  Foo(int32& x) cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldc.i4.s   25
  IL_0004:  stind.i4
  IL_0005:  ret
} // end of method Program::Foo

以及 Foo(int*)

的代码
.method private hidebysig static void  Foo(int32* x) cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldc.i4.s   25
  IL_0004:  stind.i4
  IL_0005:  ret
} // end of method Program::Foo

所以我的问题是,为什么签名不同而生成的代码相同?不能用指针做同样的工作吗?

注意:我编译了这段代码Visual Studio 2015 PreviewDebug Mode

这两种方法的实现具有相同 IL 的原因是 stind.i4 opcode:

stores an int32 value at the supplied address (type native int, *, or &).

这意味着 OpCode 适用于指针和引用,因为它们实际上都只是存储位置的地址。由于正在完成的工作除了传入的类型外是相同的,但应用相同的 OpCode,所以方法主体最终是相同的。

但这并不意味着它们是可以互换的方法。它们需要以不同的方式调用,这就是签名不同的原因。此外,这只是中间语言 - 它们可能会被 JIT 编译成不同的机器码。

请记住,IL 不是编译的最终结果,它是一种可移植到最终编译阶段的中间格式。

IL 指令根据操作数堆栈顶部项目的类型更改行为。 JIT 编译器执行类型分析并确定 stind 是处理指针还是引用,就像它确定 add 是处理整数、浮点数还是指针一样。