参数真的是按值传递吗? [复制]

Posted

技术标签:

【中文标题】参数真的是按值传递吗? [复制]【英文标题】:Is really argument passing by value? [duplicate] 【发布时间】:2017-02-22 00:47:05 【问题描述】:

我正在阅读 John Skeet 的书 "C# in Depth"。他在第 74 页上说,每个人都假设通过引用传递给函数的参数,同时它是通过值传递的,并且作为示例,他展示了这段代码,该代码必须证明调用代码中的 StringBuilder 没有改变。与此同时,我们的函数 StringBuilder 实例内部发生了变化。

private static void SayHello(StringBuilder s)
 
   s.AppendLine("Hello");
 

但我的实验表明 StringBuilder 对象发生了变化——我们将在控制台中看到“Hello”。这里有什么问题?或者我对这个例子的理解有什么问题?

       private static void Main(string[] args)
        
           var s  = new StringBuilder();
            Console.WriteLine(s.ToString());
            SayHello(s);
            Console.WriteLine(s.ToString());
            Console.ReadLine();
        

        private static void SayHello(StringBuilder s)
        
            s.AppendLine("Hello");
        

【问题讨论】:

我不想让你感到惊讶,但没有人在第 74 页上打开过 C# 深度。StringBuilder 是引用类型。告诉我们哪些段落让您感到困惑。 s.ToString() 在每次调用时都会为您提供一个新的字符串对象。 s StringBuilder 引用在整个示例中保持不变。 你把object的地址传给另一个方法,结果被修改了,你期待什么? @Timeless,我期待引用对象的行为,而不是 John 书中解释的值对象行为。这就是我问这个问题的原因。 又一个:***.com/questions/7321602/… 【参考方案1】:

看完页面,他说的是这个。

现在请记住,引用类型变量的值是 参考,而不是对象本身。您可以更改内容 参数引用的对象,参数本身不存在 通过引用传递。

    public static void Main()
    
        var s = new StringBuilder();
        Console.WriteLine(s.ToString());
        SayHello(s);
        Console.WriteLine(s.ToString());
        Console.ReadLine();
    

    private static void SayHello(StringBuilder s)
    
        s.AppendLine("Hello");
        s = null;
    

    //output will be Hello

调用此方法时,参数值(对 StringBuilder) 是按值传递的。如果我要改变 方法中的构建器变量——例如,使用语句 builder = null;—相反,调用者不会看到该更改 神话。

几乎他解释说你不能在 SayHello 中销毁对象StringBuilder,你可以改变这个对象的内容。我也不知道。

编辑:关于您的评论

当你在 SayHello 中传递 s 时,你正在创建新的引用,并且 Main 方法和 SayHello 中的引用都引用同一个对象。但它们就像 2 个不同的引用一样存在。

如果你写在

private static void SayHello(StringBuilder s)

    s.AppendLine("Hello");
    s = new StringBuilder();


SayHello 中 s 的引用将指向另一个对象,但这不会改变 Main 方法中的任何内容,因为该引用指向您写 Hello 的前一个对象。所以再次输出Hello。

如果你想传递准确的参考,你应该使用ref

所以如果你写

private static void SayHello(ref StringBuilder s)

     s.AppendLine("Hello");
     s = null;
     //you will receive an exception in Main method, because now the value in the reference is null.

【讨论】:

好吧,我还是不明白——我们得到了对象的引用,我们改变了对象,所以这必须改变对象实例并且这些改变应该是可见的,不管我们做了什么——添加字符串还是传递null? @andrey.shedko 我进行了编辑。 谢谢,很好的解释。 P.S.您答案的第二部分是真正的答案,但不是第一部分,您在谈论破坏对象。【参考方案2】:

考虑以下类比:

    您购买了一套新公寓 (new StringBuilder()),并获得了一个允许您访问的钥匙 (s)。 您聘请了一名清洁工 (s.AppendLine),然后将您的钥匙副本交给他,以便他可以进入您的公寓并进行清洁。 清洁工使用您的钥匙副本,打扫您的公寓,并且因为他喜欢这样做,所以重新编程钥匙以打开其他公寓。 你完成了你的日常工作,然后回家了。您的钥匙可以很好地打开您公寓的门,您会发现它很干净。

这基本上就是按值传递引用类型时发生的情况。

现在考虑以下几点:

    您购买了一套新公寓 (new StringBuilder()),并获得了允许您访问的钥匙 (s)。 你雇了一个清洁工 (s.AppendLine),然后你给他你的钥匙,这样他就可以打扫你的公寓了。 清洁工清理了你的房间,因为他喜欢,所以重新编程了你给他的钥匙来打开其他公寓。他把你的钥匙留在了门垫下面。 你完成了你的日常工作,然后回家了。你试图用你的钥匙,但门打不开。透过窗户,您可以看到您的公寓很干净。

当您通过引用传递引用类型时会发生这种情况。

【讨论】:

以上是关于参数真的是按值传递吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 是按引用传递还是按值传递? [复制]

js函数的参数都是按值传递怎么理解

Python 的布尔值是按值传递的吗?

Ruby 是按值传递还是按引用传递? [复制]

java中的参数传递是按引用传递还是按值传递

作为参数的函数是不是必须按值传递? [复制]