【问题标题】:What happens on the stack with ref parameters in c#?c# 中带有 ref 参数的堆栈会发生什么?
【发布时间】:2011-09-28 10:52:37
【问题描述】:

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

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

【问题讨论】:

  • @bemused:好吧,如果您将“正常通过”视为“默认值”,那么这很公平,因为默认值是“按值”。我不会说 by-ref “一直在代码中”发生 - ref 参数在我的经验中非常罕见。

标签: c# stack ref-parameters


【解决方案1】:

通过引用传递的不是 object - 它是 variable

基本上它为调用端用作参数的变量和你调用的方法中的参数取别名:

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 看到,反之亦然。 (请注意,在这种情况下,它是调用者中的一个局部变量,但它不一定是 - 如果您通过引用传递了一个 instance 变量,那么Bar 可能会调用另一个方法更改相同的变量,然后y 将被视为“神奇地”更改...)

更多关于C#中的参数传递,请参考我的article on the subject

【讨论】:

    【解决方案2】:

    By reference 表示你可以更改传递给项目的原始变量。它基本上是传递栈上变量的地址而不是变量值。

    IL 转储:

    正如您实际询问 堆栈上实际发生的情况,这里是 by refby value 方法的 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";
    }
    

    【讨论】:

      【解决方案3】:

      当你通过值传递某些东西时;在调用函数之前,会制作一个值类型的副本并将其放入堆栈。当你通过引用传递一些东西时,它的地址被压入堆栈而不是创建一个副本,当你在函数中修改对象时,原始对象被修改而不是副本。

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • +1 因为你说的是​​真的,但读起来很混乱。
        • @Kirk Broadhurst:upvote 的工具提示说“答案很有用”,而不仅仅是 true :)
        猜你喜欢
        • 1970-01-01
        • 2014-04-16
        • 2020-08-22
        • 2017-05-28
        • 2010-11-22
        • 1970-01-01
        • 2019-10-06
        • 2020-04-19
        • 1970-01-01
        相关资源
        最近更新 更多