字符串和StringBuilder之间有什么区别?

6
我知道 string 是不可变的,而 StringBuilder 是可变的。但是谁能解释以下代码的输出?既然它们都是引用类型,为什么会有不同的结果呢?
String s1 = "hello";
String s2 = "hello";
Console.WriteLine(s1 == s2); //true
Console.WriteLine(Object.ReferenceEquals(s1, s2)); //true

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");
Console.WriteLine(sb1 == sb2); //false
Console.WriteLine(Object.ReferenceEquals(sb1,  sb2)); //false

1
你正在比较两个不同的 StringBuilder 对象引用。它们是两个独立的对象。 - wkl
不仅因为这是一个出色的问题,而且因为您提出得非常清晰,并且提供了完整的代码示例并进行了完美的注释,我要给您点赞。 - Adam Liss
1
这个问题实际上与 StringBuilder 没有任何关系。它真正涉及的是“为什么用包含相同字符序列的两个字符串字面量初始化的两个字符串变量比较时会被视为同一对象”? - John Saunders
使用 string s2 = "hel" + "lo",可以看出它与类型无关。 - Hans Passant
4个回答

10
由于两者都是引用类型,为什么它们有不同的结果?
这是因为 `string` 对象被高度优化。特别是,由于它们是不可变的,编译器可以将它们合并以防止重复。
如果您有两个不同的 `string` 对象,它们都代表完全相同的字符序列(就像您的示例一样),编译器将会识别并维护实际字符串对象的一个实例。
结果是,对于编译器而言,`s1` 和 `s2` 对象实际上是同一个对象,甚至在内存中引用相同的位置。
这种记录发生在一个叫做“intern table”的东西背后,但这并不是你需要担心的事情。重要的是,所有字符串字面量默认都被编译器放入了“intern table”。
对于 `StringBuilder` 对象,同样的事情并不会发生,因为它们是可变的。它们被设计用来允许您修改一个字符串对象,因此优化并没有太多意义。这就是为什么 `sb1` 和 `sb2` 对象实际上被视为两个不同的对象。
简单来说:如果您想要一个单独的不可变字符串,或者希望默认使用字符串,请使用 `string`。只有当您想要在循环或其他相对较短的代码段中多次修改同一个字符串时,请使用 `StringBuilder`。
相关阅读:优化 C# 字符串性能

4
当您声明时,
String s1 = "hello";
String s2 = "hello";

编译器很聪明地知道这两个字符串是相同的(而且永远都是相同的),因此它只存储了一个"hello",并将s1和s2创建为指向相同物理内存的别名。后来,当您测试相等性时,它们相等,因为它们实际上是同一个变量。
另一方面,当您声明
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");

编译器创建了两个变量(因为它们都是可变的,但恰好被初始化为相同的值)。它将字符串"hello"复制到每个变量中,但现在有两个副本,因为每个变量以后可能会更改。因此,即使它们的内容相同,它们也是两个不同的实体,驻留在不同的物理内存位置,所以对象等价性测试失败。

3

默认情况下,当比较两个引用类型的对象时,只有当两个引用都指向同一个对象实例时结果才为true。因为sb1和sb2引用的是两个不同的对象,所以比较StringBuilder的结果为false。

但是String类重写了等号运算符,使其按值而非引用比较对象,这种行为对程序员来说非常直观和可预期。这就解释了为什么s1 == s2返回true。

Object.ReferenceEquals(s1, s2)也返回true的原因(尽管s1和s2看起来引用了不同的字符串实例)是因为string interning。它导致CLR将相同的字符串字面量(例如您示例中的“hallo”和“hallo”)在应用程序内部字符串池中仅放置一次,因此s1和s2实际上引用相同的字符串实例“hallo”。这是可能的,因为字符串是不可变的。


1

当你在进行以下操作时,你正在比较两个不同的StringBuilder对象而不是它们的值:

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");
Console.WriteLine(sb1 == sb2); //false

这是如何比较它们的值:

Console.WriteLine(sb1.ToString() == sb2.ToString());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接