参数真的是按值传递吗? [复制]
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
),然后你给他你的钥匙,这样他就可以打扫你的公寓了。
清洁工清理了你的房间,因为他喜欢,所以重新编程了你给他的钥匙来打开其他公寓。他把你的钥匙留在了门垫下面。
你完成了你的日常工作,然后回家了。你试图用你的钥匙,但门打不开。透过窗户,您可以看到您的公寓很干净。
当您通过引用传递引用类型时会发生这种情况。
【讨论】:
以上是关于参数真的是按值传递吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章