1

以下代码如何工作?

public void SomeMethod()
{
    StringBuilder sb = new StringBuilder();
    AppendFoo(sb);
    String foo = sb.ToString(); // foo is "foo"

    String s = String.Empty;
    AppendBar(s);
    String bar = s; // bar is empty 
}

public void AppendFoo(StringBuilder x)
{
    x.Append("Foo");
}

public void AppendBar(String x)
{
    x = x + "Bar";
}

如果两者StringBuilder都是String引用类型,为什么字符串对象在通过AppendBar方法传递时没有改变,而 StringBuilder 对象在传递给AppendFoo方法时改变,因为方法的两个参数都将引用类型作为参数?

4

6 回答 6

13

暂时忽略字符串是不可变的这一事实 - 这有点像红鲱鱼。重要的一点是:

x.Append(...);

x = x + ...;

仔细观察它们:第一个作用引用的对象x,改变StringBuilder. 第二个是更改值x以引用不同的对象(新字符串)。它不会改变现有对象的内容。(事实上​​它不能,因为字符串是不可变的,但同样的逻辑仍然适用。)

更改x方法内的值不会更改用于初始化的参数的值x

关键是要区分改变变量的值和改变它所引用的对象的内容。一旦你有了这种差异,其余的就应该到位。

在我关于参数传递的文章中阅读有关此和参数的更多信息,在另一篇文章中阅读有关引用类型与值类型的更多信息。

于 2009-11-21T18:27:16.060 回答
2

字符串是不可变的——一旦创建就无法更改。

appendBar 中发生的事情是:

x= x+“条”

创建一个新字符串(使用新值)并将引用 x 设置为它。(实际实现取决于编译器)

但是调用代码中的引用 s 仍然指向原来的字符串对象。

于 2009-11-21T18:20:34.170 回答
1

在第一次调用中AppendFoo,您通过调用它的方法成员来更改参数。

在第二次调用中AppendBar,您为参数分配了新值。它不反映原始参数(除非您通过 ref 调用它)

例如,如果第一次调用是:

public void AppendFoo(StringBuilder x)
{
    x = new StringBuilder();
    x.Append("Foo");
}

你会得到同样的结果。

很容易认为它与字符串不可变的事实有关,但与它无关。它只是为参数分配新值,因此它不会更改发送给方法的原始对象。

于 2009-11-21T18:21:01.517 回答
1

字符串是引用类型,但在 C# 中它们是不可变的,这意味着一旦分配,它们就永远不会改变它们的内容。当你说的时候你在做什么

x = x + "Bar";

is '创建一个包含 x+"Bar" 的新字符串,并将其分配给 x 的本地引用。因为您没有将字符串作为 'ref' 参数传递,所以分配给本地引用不会影响函数外部的引用。

于 2009-11-21T18:21:28.287 回答
0

字符串完全不可变并不重要,您在 AppendBar 函数中缺少一个 ref 参数。

public void AppendBar(ref String x)
{
    x = x + "Bar";
}

逻辑是一样的,如果你的 AppendFoo 是

public void AppendFoo(StringBuilder x)
{
    x = new StringBuilder();
    x.Append("Foo");
}

它也不起作用...如果要更改引用的值,则需要将 x var 声明为 ref。

于 2009-11-21T18:35:09.757 回答
0

.NET 字符串实际上是一种不可变的数据类型,这意味着一旦初始化一个字符串对象,该字符串对象就永远不会改变。看起来像修改字符串内容的运算符,实际上是创建新字符串,然后将存储在变量中的地址更新为新创建的字符串的地址。旧字符串变为未引用。

因此,在将字符串传递给函数的情况下,AppendBar(string x)例如,当您这样做时x = x + "Bar",.NET 运行时会分配足够的内存来存储组合文本。示例中为空的原始文本和“Bar”被复制到新的字符串实例中。 x更新为指向新字符串实例的地址。但是,由于作为副本s传递给,因此调用方方法中的值不会更新为指向新的字符串实例。它仍在存储旧的地址,即空字符串一。因此,在调用该函数后,您对 的引用仍然会产生空字符串。AppendBar(string x)ss

您可以更改函数的签名AppendBar(ref string x)以使传递给函数的字符串发生更改。在语句之后AppendBar(ref s);s“is”然后是“Bar”。

于 2009-11-21T18:54:16.223 回答