2

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

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

4

4 回答 4

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

对比

.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

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

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

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

void ByValue(string s)
{
    s = "New value";
}
于 2011-09-28T10:55:02.983 回答
5

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

基本上,它为调用方用作参数的变量以及您调用的方法中的参数起别名:

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

public void Bar(ref int y)
{
    y = 20;
}

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

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

于 2011-09-28T11:00:29.130 回答
1

当您按值传递某些东西时;在调用函数之前制作值类型的副本并将其放入堆栈。当您通过引用传递某些内容时,该地址被推入堆栈而不是创建副本,并且当您修改函数中的对象时,原始对象将被修改而不是副本。

它的工作方式是当编译器看到参数由 ref 传递时,编译器会将您对变量的引用转换为间接内存访问。

例如让我们假设在内存位置 100 你有一个整数 123;现在,当您调用一个按值接受的函数(这是默认值)时,将在调用该函数之前制作 123 的副本并将其推送到堆栈上,这意味着该副本现在将位于(比如说)160 地址。但是,当您通过引用传递某些内容时,地址 100 将被压入堆栈并驻留在位置 160。

现在生成的指令将读取 160 以获取对象的位置,然后在 100 处修改数据。间接将发生与使用 * 运算符时对指针所做的相同。

于 2011-09-28T11:02:11.307 回答
-1

通过 ref 意味着您可以为传递的对象创建新实例,按值不能只更改对象属性

于 2011-09-28T10:56:00.487 回答