由于字符串是不可改变的,具有相同字符串值的变量是否指向同一个字符串对象?

16

a)

        string s  = "value";
        string s1 = "value";

s和s1引用变量指向同一个字符串对象吗?(我假设这是因为字符串是不可变的)

b) 我知道等号运算符(==,>等)已被重新定义为比较字符串对象的值,但使用静态方法Object.Equals()和Object.ReferenceEquals()比较两个字符串是否也是如此?

谢谢


2
我不是专家,但请查看有关字符串内部化的链接。 http://en.csharp-online.net/CSharp_String_Theory%E2%80%94String_intern_pool - ChaosPandion
10
请参考我的这篇文章,其中有一些额外的评论:http://blogs.msdn.com/ericlippert/archive/2009/09/28/string-interning-and-string-empty.aspx - Eric Lippert
@Eric - 很棒的阅读,感谢您发布链接。 - ChaosPandion
@Eric:另外,字符串并不是真正的不可变的,对吧?也就是说我们不能依赖这个细节? - Joan Venge
1
@Joan:字符串在实际目的上是不可变的。是的,你可以使用不安全的代码并改变与字符串相关联的内存,但你也可以使用不安全的代码并做任何事情。一旦你使用了不安全的代码,那么所有的保证都将被取消。 - Eric Lippert
显示剩余3条评论
3个回答

21

不,不是所有具有相同值的字符串都是相同的对象引用。

编译器生成的字符串将全部被汇总,并且是相同的引用。运行时生成的字符串默认情况下不会被汇总并且将是不同的引用。

var s1 = "abc";
var s2 = "abc";
var s3 = String.Join("", new[] {"a", "b", "c"});
var s4 = string.Intern(s3); 
Console.WriteLine(ReferenceEquals(s1, s2)); // Returns True
Console.WriteLine(ReferenceEquals(s1, s3)); // Returns False
Console.WriteLine(s1 == s3); // Returns True
Console.WriteLine(ReferenceEquals(s1, s4)); // Returns True

请注意上面的一行代码,您可以使用String.Intern(string)强制将字符串内部化,从而允许您对某些检查使用对象相等性而不是字符串相等性,这样会更快。一个常见的使用场景是在生成的 XML 序列化器代码和名称表中。


1
+1 表示需要澄清在运行时创建的字符串必须手动进行内部化。 - ChaosPandion
可能值得添加以下代码: var s4 = string.Intern(s3); Console.WriteLine(ReferenceEquals(s1, s4)); // 返回 True - Dolphin

3
是的,它们将指向相同的字符串,因为它们都被定义为字符串字面量。如果您以编程方式创建字符串,则必须手动将字符串合并。
这是因为.NET框架将程序中的字符串字面量合并到intern池中。您可以使用String.Intern检索此引用,或手动将自己运行时生成的字符串合并。
有关更多详细信息,请参阅内部文档

因此,具有特定值的文字字符串实例在系统中仅存在一次。

例如,如果将相同的文字字符串分配给多个变量,则运行时从intern池中检索对文字字符串的相同引用,并将其分配给每个变量


2
然而,这是通过实现而非规范来实现的。 - Nick Larsen
是的 - 但目前实现确实对字面值执行了此操作。 - Reed Copsey
我在我的回答中添加了一些澄清。 - Reed Copsey

2
使用当前的CLR,相同的文本字符串指向同一个实际对象。这个过程被称为内部化,并适用于所有编译时字符串。
在运行时创建的字符串默认情况下不会被内部化,但可以通过调用string.Intern将它们添加到内部化集合中。
有关.NET字符串存储方式的详细说明,请参见我对这个问题的回答

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