8

第二个 ReferenceEquals 调用返回 false。为什么 s4 中的字符串没有被实习?(我不关心 StringBuilder 相对于字符串连接的优势。)

string s1 = "tom";
string s2 = "tom";


Console.Write(object.ReferenceEquals(s2, s1)); //true

string s3 = "tom";
string s4 = "to";
s4 += "m";

Console.Write(object.ReferenceEquals(s3, s4)); //false

当我这样做时String.Intern(s4);,我仍然是假的。

在这里,s3 和 s4 都被实习了,但它们的引用不相等?

string s3 = "tom";
string s4 = "to";
s4 += "m";
String.Intern(s4);

Console.WriteLine(s3 == s4); //true
Console.WriteLine(object.ReferenceEquals(s3, s4)); //false
Console.WriteLine(string.IsInterned(s3) != null);  //true (s3 is interned)
Console.WriteLine(string.IsInterned(s4) != null);  //true (s4 is interned)
4

6 回答 6

17

in 中的字符串s4被保留。但是,当您执行时s4 += "m";,您创建了一个不会被实习的新字符串,因为它的值不是字符串文字,而是字符串连接操作的结果。结果,s3s4是两个不同内存位置中的两个不同字符串实例。

有关字符串实习的更多信息,请查看此处,特别是最后一个示例。当你这样做时String.Intern(s4),你确实在实习字符串,但你仍然没有在这两个实习字符串之间执行引用相等测试。该String.Intern方法返回内部字符串,因此您需要执行以下操作:

string s1 = "tom";
string s2 = "tom";

Console.Write(object.ReferenceEquals(s2, s1)); //true 

string s3 = "tom";
string s4 = "to";
s4 += "m";

Console.Write(object.ReferenceEquals(s3, s4)); //false

string s5 = String.Intern(s4);

Console.Write(object.ReferenceEquals(s3, s5)); //true
于 2010-04-24T23:44:27.257 回答
3

字符串是不可变的。这意味着它们的内容无法更改。

当您在s4 += "m";内部执行此操作时,CLR 会将字符串复制到内存中包含原始字符串和附加部分的另一个位置。

请参阅MSDN 字符串参考

于 2010-04-24T23:43:30.503 回答
2

首先,到目前为止,关于不可变字符串的所有内容都是正确的。但是有一些重要的东西没有写出来。编码

string s1 = "tom";
string s2 = "tom";
Console.Write(object.ReferenceEquals(s2, s1)); //true

显示真的“真”,但只是因为一些小的编译器优化或喜欢这里,因为 CLR 忽略 C# 编译器属性(参见“通过 C# 的 CLR”一书)并且只"tom"在堆中放置一个字符串。

其次,您可以使用以下几行来解决这种情况:

s3 = String.Intern(s3);
s4 = String.Intern(s4);
Console.Write (object.ReferenceEquals (s3, s4)); //true

函数String.Intern计算字符串的哈希码并在内部哈希表中搜索相同的哈希。因为它找到了这个,所以它返回对已经存在的String对象的引用。如果内部散列表中不存在该字符串,则制作该字符串的副本并计算散列。垃圾收集器不会为字符串释放内存,因为它被哈希表引用。

于 2010-04-25T00:16:22.200 回答
2

来源:https ://blogs.msdn.microsoft.com/ericlippert/2009/09/28/string-interning-and-string-empty/

字符串实习是编译器的一种优化技术。如果在一个编译单元中有两个相同的字符串字面量,则生成的代码可确保在程序集中为该字面量的所有实例(用双引号括起来的字符)只创建一个字符串对象。

我来自 C# 背景,所以我可以举一个例子来解释:

object obj = "Int32";
string str1 = "Int32";
string str2 = typeof(int).Name;

以下比较的输出:

Console.WriteLine(obj == str1); // true
Console.WriteLine(str1 == str2); // true    
Console.WriteLine(obj == str2); // false !?

注1:对象通过引用进行比较。

Note2 :typeof(int).Name 是通过反射方法评估的,因此它不会在编译时进行评估。这里这些比较是在编译时进行的。

结果分析: 1) 正确,因为它们都包含相同的文字,因此生成的代码将只有一个引用“Int32”的对象。见注 1

2) true 因为两个值的内容被检查是相同的。

3) FALSE 因为 str2 和 obj 没有相同的文字。见注 2

于 2017-09-24T05:03:46.893 回答
1

在 C# 中,每个字符串都是一个不同的对象,并且不能被编辑。您正在创建对它们的引用,但每个字符串都是不同的。行为一致且易于理解。

我可以建议检查StringBuilder类以在不创建新实例的情况下操作字符串吗?对于你想用字符串做的任何事情,它应该足够了。

于 2010-04-24T23:44:52.320 回答
0

当比较两个对象而不是字符串时,不调用字符串相等运算符,因为它是没有多态性的静态方法。

这是一个测试:

static void Test()
{
  object o1 = "a";
  object o2 = new string("a".ToCharArray());

  string o3 = "a";
  string o4 = new string("a".ToCharArray());

  object o5 = "a"; // Compiler optimization addr(o5) = addr(o6)
  object o6 = "a";

  string o7 = "a"; // Compiler optimization addr(o7) = addr(o8)
  string o8 = "a";

  Console.WriteLine("Enter same text 4 times:");

  object o9 = Console.ReadLine();
  object o10 = Console.ReadLine();

  string o11 = Console.ReadLine();
  string o12 = Console.ReadLine();

  Console.WriteLine("object arr   o1  == o2  ? " + ( o1 == o2 ).ToString());
  Console.WriteLine("string arr   o3  == o4  ? " + ( o3 == o4 ).ToString());
  Console.WriteLine("object const o5  == o6  ? " + ( o5 == o6 ).ToString());
  Console.WriteLine("string const o7  == o8  ? " + ( o7 == o8 ).ToString());
  Console.WriteLine("object cnsl  o9  == o10 ? " + ( o9 == o10 ).ToString());
  Console.WriteLine("string cnsl  o11 == o12 ? " + ( o11 == o12 ).ToString());
  Console.WriteLine("o1.Equals(o2) ? " + o1.Equals(o2).ToString());
  Console.WriteLine("o3.Equals(o4) ? " + o3.Equals(o4).ToString());
  Console.WriteLine("o5.Equals(o6) ? " + o5.Equals(o6).ToString());
  Console.WriteLine("o7.Equals(o8) ? " + o7.Equals(o8).ToString());
  Console.WriteLine("o9.Equals(o10) ? " + o9.Equals(o11).ToString());
  Console.WriteLine("o11.Equals(o12) ? " + o11.Equals(o12).ToString());
}

结果:

object arr   o1  == o2  ? False
string arr   o3  == o4  ? True
object const o5  == o6  ? True
string const o7  == o8  ? True
object cnsl  o9  == o10 ? False
string cnsl  o11 == o12 ? True
o1.Equals(o2) ? True
o3.Equals(o4) ? True
o5.Equals(o6) ? True
o7.Equals(o8) ? True
o9.Equals(o10) ? True
o11.Equals(o12) ? True

https://referencesource.microsoft.com/#mscorlib/system/string.cs

于 2019-10-07T16:21:09.877 回答