奇怪的字符串文字比较

18

在深入学习C#时,我遇到了一个小问题,涉及到对象引用相等性。

假设我有两个字符串:

String a = "Hello world!";
String b = "Bonjour le monde";
bool equals = ReferenceEquals(a, b);  // ******************* (1)
b = "Hello world!";
equals = ReferenceEquals(a, b);       // ******************* (2)

(1)false,这是预期结果。 文档中说明了 ReferenceEquals 比较的是实例。

但是:

  • 为什么 (2) 返回 true?
  • 字符串 ab 不是同一个对象吗?如果是,那么它们怎么变成相同的了,因为我从来没有明确地执行过 a=b

3
字符串驻留有一些奇怪的边角情况;如果您感兴趣,请参阅我关于此主题的文章:http://blogs.msdn.com/b/ericlippert/archive/2009/09/28/string-interning-and-string-empty.aspx - Eric Lippert
4个回答

26
这是因为字符串驻留的原因。
公共语言运行时通过维护一个称为“intern pool”的表格来节省字符串存储空间,该表格包含在程序中声明或以编程方式创建的每个唯一文字字符串的单个引用。因此,系统中仅存在具有特定值的文字字符串实例一次。
例如,如果您将相同的文字字符串分配给几个变量,则运行时从intern pool检索对文字字符串的相同引用,并将其分配给每个变量。

10

字符串字面量会被 .NET 运行时自动 内部化。这意味着具有相同值的字符串字面量共享同一个字符串实例,以减少内存使用并提高性能。这是一种安全的优化方式,因为字符串是不可变的。

您的代码编译成类似以下的 CIL 指令:

IL_0001: ldstr "Hello world!"
IL_0006: stloc.0
IL_0007: ldstr "Bonjour le monde"
IL_000c: stloc.1
etc...

根据ECMA规范ldstr("加载文字字符串")指令的文档:

默认情况下,CLI保证两个引用具有相同字符序列的元数据标记的 ldstr 指令的结果返回完全相同的字符串对象(这个过程称为“字符串内联”)。可以使用 System.Runtime.CompilerServices.CompilationRelaxationsAttributeSystem.Runtime.CompilerServices.CompilationRelaxations.NoStringInterning 控制此行为。

您还可以通过调用 String.Intern 方法来自己对字符串进行内联。


5

字符串字面量大部分时间都是相同的对象,因为它们是常量且不可变的。

取自microsoft docs

每个字符串字面量并非必然导致一个新的字符串实例。当两个或多个字符串字面量根据字符串相等性运算符(第7.9.7节)是等效的时,这些字符串字面量引用同一个字符串实例。例如,由以下代码生成的输出:

class Test
{
   static void Main() {
      object a = "hello";
      object b = "hello";
      System.Console.WriteLine(a == b);
   }
}

因为这两个字面量引用同一个字符串实例,所以是True。

+1。谢谢你的回答。你说的“大多数情况”是什么意思?有没有例外情况它们不相同?字符串“Bonjour le monde”会发生什么?它会被垃圾回收吗? - GETah
大多数情况下 - 考虑情况 b = new string("hello");(我认为在 C# 中是允许的)被垃圾回收 - 不会,我认为它将留在字符串池中。 - MByD

3

.NET维护字符串池,因为它们是不可变的。您不必担心它,因为clr本身会重复使用它们。


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