使用c#中的ref参数在堆栈上发生了什么?

Exi*_*tos 1 c# stack ref-parameters

我正在阅读有关WCF和IDispatchMessageInspector的一些C#文档,并且该接口定义了一个通过引用传递的"Message"对象,以便可以对其进行操作.

当您通过ref传递某些东西而不是正常传递时,堆栈上实际发生了什么?

Gon*_*ing 5

通过引用意味着您可以更改传递给项目的原始变量。它基本上传递堆栈上变量的地址而不是变量值。

IL 转储:

正如您实际上询问堆栈上实际发生的情况一样,这里是按引用按值方法的 IL 转储:

.method private hidebysig instance void  ByRef(string& s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "New value"
  IL_0007:  stind.ref
  IL_0008:  ret
} // end of method Class1::ByRef
Run Code Online (Sandbox Code Playgroud)

.method private hidebysig instance void  ByValue(string s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "New value"
  IL_0006:  starg.s    s
  IL_0008:  ret
} // end of method Class1::ByValue
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,主要区别在于参数类型(string&而不是string),并且它执行额外的步骤来间接加载和存储值。

简单的C#源码如下所示,供参考:

void ByRef(ref string s)
{
    s = "New value";
}

void ByValue(string s)
{
    s = "New value";
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 5

它不是通过引用传递的对象 - 它是变量.

基本上,它将用作调用方参数的变量和您调用的方法中的参数别名:

public void Foo()
{
    int x = 10;
    Bar(ref x);
    Console.WriteLine(x); // Prints 20
}

public void Bar(ref int y)
{
    y = 20;
}
Run Code Online (Sandbox Code Playgroud)

在这里,xy在本质上是相同的变量-它们指向同一个存储位置.所做的更改x是可见的y,反之亦然.(注意,在这种情况下,它是调用者中的局部变量,但它不一定是 - 如果您通过引用传递了一个实例变量,那么Bar可能会调用另一个更改同一变量的方法,然后y会被看到"神奇地"改变...)

有关C#中参数传递的更多信息,请参阅我关于该主题的文章.