参数真的按值传递吗?
Is really argument passing by value?
我正在读 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");
}
看完页面,他说的是这个
Now remember that the value of a reference type variable is the
reference, not the object itself. You can change the contents of the
object that a parameter refers to without the parameter itself being
passed by reference.
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
When this method is called, the parameter value (a reference to a
StringBuilder) is passed by value. If I were to change the value of
the builder variable within the method—for example, with the statement
builder = null;—that change wouldn’t be seen by the caller, contrary
to the myth.
他几乎解释说你不能破坏 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.
}
考虑以下类比:
- 您购买了一套新公寓 (
new StringBuilder()
) 并获得了一把钥匙 (s
),您可以使用它。
- 你雇了一个清洁工 (
s.AppendLine
),你给了他你钥匙的副本,这样他就可以进入你的公寓并打扫它。
- 清洁工使用您的钥匙副本,打扫您的公寓,并且因为他喜欢,所以重新编程钥匙以打开其他公寓。
- 你完成日常工作回家。你的钥匙打开你公寓的门就好了,你发现它很干净。
这基本上就是按值传递引用类型时发生的情况。
现在考虑以下问题:
- 您购买了一套新公寓 (
new StringBuilder()
) 并获得了一把钥匙 (s
),您可以使用它。
- 你雇了一个清洁工 (
s.AppendLine
),你把你的钥匙给了他,这样他就可以打扫你的公寓了。
- 清洁工打扫您的房间,因为他喜欢,所以重新编程您给他的钥匙以打开其他公寓。他把你的钥匙放在门垫下面了。
- 你完成日常工作回家。您尝试使用您的钥匙,但门打不开。通过window你可以看到你的公寓很干净。
这就是通过引用传递引用类型时发生的情况。
我正在读 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");
}
看完页面,他说的是这个
Now remember that the value of a reference type variable is the reference, not the object itself. You can change the contents of the object that a parameter refers to without the parameter itself being passed by reference.
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
When this method is called, the parameter value (a reference to a StringBuilder) is passed by value. If I were to change the value of the builder variable within the method—for example, with the statement builder = null;—that change wouldn’t be seen by the caller, contrary to the myth.
他几乎解释说你不能破坏 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.
}
考虑以下类比:
- 您购买了一套新公寓 (
new StringBuilder()
) 并获得了一把钥匙 (s
),您可以使用它。 - 你雇了一个清洁工 (
s.AppendLine
),你给了他你钥匙的副本,这样他就可以进入你的公寓并打扫它。 - 清洁工使用您的钥匙副本,打扫您的公寓,并且因为他喜欢,所以重新编程钥匙以打开其他公寓。
- 你完成日常工作回家。你的钥匙打开你公寓的门就好了,你发现它很干净。
这基本上就是按值传递引用类型时发生的情况。
现在考虑以下问题:
- 您购买了一套新公寓 (
new StringBuilder()
) 并获得了一把钥匙 (s
),您可以使用它。 - 你雇了一个清洁工 (
s.AppendLine
),你把你的钥匙给了他,这样他就可以打扫你的公寓了。 - 清洁工打扫您的房间,因为他喜欢,所以重新编程您给他的钥匙以打开其他公寓。他把你的钥匙放在门垫下面了。
- 你完成日常工作回家。您尝试使用您的钥匙,但门打不开。通过window你可以看到你的公寓很干净。
这就是通过引用传递引用类型时发生的情况。