以下示例来自Jon Skeet的文章“Parameter passing in C#”。
我的问题是:为什么变量y在第一个示例中是 NOT null,而我们在第二个示例中看到它已被更改:
1 -
void Foo (StringBuilder x)
{
x = null;
}
...
StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y==null);
2 -
void Foo (StringBuilder x)
{
x.Append (" world");
}
...
StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (y);
Console.WriteLine (y);
谢谢
答案 0 :(得分:9)
y
不 null,正是因为参数是按值传递的。计算参数表达式(只是y
),并将其值(对StringBuilder
的引用复制到参数变量(x
)中作为初始值。
更改x
的值而不是会更改y
的值。
第二个示例不会更改参数(x
)的值 - 它会更改x
引用的对象中的数据。 y
的值仍然没有改变:它仍然引用相同的StringBuilder
对象,只是该对象现在包含不同的数据。
如果我给你一张带有我家庭住址的纸张,然后你去那个地址并把房子涂成红色,你就不会改变那张纸,是吗?如果你做了在纸上划掉我的地址(如第一个例子中那样),这不会改变我的我的地址的想法 - 就像改变价值一样x
的{{1}}不会改变y
的值。
答案 1 :(得分:3)
在两个示例中,对StringBuilder的引用都是按值传递的
将y
视为实际StringBuilder地址的持有者
地址本身被复制到Foo作为堆栈参数,因此第一个示例中的赋值x = null
更改了复制的地址,而不是y
中存储的实际地址。
在第二个示例中,x.Append
指的是y指向并更改它的相同实例,因此更改是可见的。
答案 2 :(得分:2)
啊,这更有意义......这是因为指针如何工作。在这两个示例中,您都将指针的副本传递给函数。想象一下,y
是一张纸上写着“左边的第二扇门”,这是我们想要存放字符串的房间。
现在,在第一个例子中,你正在制作那张纸的副本,并且该方法正在使用该副本并删除它所说的内容。由于它只是纸张的副本,原始作品不受影响。
所以现在,当我们回来并想要打印y的内容时,我们按照说明操作,找到“左边的第二扇门”,打开它并发现只有“你好”
在第二个例子中,我们仍然传递了论文的副本,但现在我们需要进行交互,所以我们按照说明进入“左边的第二扇门”,在这里我们附加字符串“world ”。所以现在,当我们回来并想要打印y的内容时,我们按照原始纸上的说明,找到“左边的第二扇门”,打开它并找到“你好世界”。 / p>
如果我们使用了ref
- 关键字,我们就不会通过该论文的副本,而是将实际的论文本身传递给该方法。因此,当该方法删除写在纸上的内容时,我们现在回来时没有找到存储字符串的门的指示。
我知道,它有点幼稚的解释,但它通常在试图解释指针时起作用。
答案 3 :(得分:2)
正如Itay 已经提到的那样,对StringBuilder的引用正在通过值传递。
当你致电Foo(y)
时,它会复制y
的值,这是数据(值)所在的内存地址,所以在x
的{{1}}中你都有副本y
变量所指向的地址。请注意,x
未引用y
它只获取y
引用类型变量值的副本。因此,通过为x
分配null, 只需将引用 从x切换到某个地址(在完整的情况下存储文本,StringBuilder数据)。由于x
只获取y
变量的副本而未引用它y
,因此不会更改y
。
在第二个示例中,您只是在y
引用的地址中操作数据,这就是在更新方法调用void Foo (StringBuilder x) // x gets copy of the address to where y is referencing
{
// x now points to null
// Remember x and y variables are located in different memory addresses and x is
// not referencing y, thus only x is updated.
x = null;
}
...
之后的原因。
让我在两个案例中添加评论
1 -
void Foo (StringBuilder x) // x gets copy of the address to where y is referencing
{
// x updates the data located in the memory address to which y is referencing
// x still points to the same address as y
x.Append (" world");
}
...
2 -
void Foo (ref StringBuilder x) // x points to y
{
x = null;
}
试试这个,它会很清楚:
{{1}}
答案 4 :(得分:0)
编辑:当我回答问题时,它表示Y为空。这个问题后来改变了。
我认为你写错了代码。我已经更新了你的代码 - 请注意,Foo参数现在通过“reference”而不是“value”传递。
class Program
{
static void Foo(ref StringBuilder x)
{
x = null;
}
static void Main(string[] args)
{
StringBuilder y = new StringBuilder();
y.Append("hello");
Foo(ref y);
Console.WriteLine(y == null); //Prints "true".
}
}
答案 5 :(得分:0)
对于(1)打印出true,代码必须如下所示:
void Foo (ref StringBuilder x)
{
x = null;
}
...
StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (ref y);
Console.WriteLine (y==null);
马里奥
答案 6 :(得分:-1)
我不知道你是否熟悉指针,但它会帮助你更好地理解它。因为参数表示指向对象的指针,所以如果将指针指定给另一个对象,则会改变指针指向的位置,因此您不会修改原始对象。
在第二个示例中,您正在调用原始指针对象的方法,因此原始对象会被修改。
Java是故意这样做的,因此对于如何分配变量没有任何歧义。如果要将变量分配给方法中定义的对象,则必须返回要分配给变量的对象(例如StringBuilder builder = createStringBuilder();
)
答案 7 :(得分:-2)
因为在第一个例子中,他将Y传递给Food,将Y设置为null:
Foo (y);
....
void Foo (StringBuilder x)
{
x = null;
}
答案 8 :(得分:-4)
所以我想我会纠正我的答案,所以没有人读过旧答案,因为它错了。我对这个问题感到有些困惑。所以答案是:
当您传入对象的对象引用时,您可以访问该对象的属性和方法。但是,如果您将输入参数更改为其他对象,则需要更改对象,因为您正在更改变量引用的内容。希望这个例子能更好地解释它,否则请阅读Jon Skeet的回答。
public static void DoSomething(MyObject x)
{
x.MyProperty = 2;
x = new MyObject(); // now we lost our reference to original object
x.MyProperty = 3; // The original object is NOT updated.
}
public static void Main(string[] args)
{
var y = MyObject();
y.MyProperty = 1;
DoSomething(y);
Console.WriteLine(y.MyProperty); // Will output "2"
}