字符串实习?

第二个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) 

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 

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

当你做s4 += "m"; 在内部,CLR将字符串复制到内存中的另一个位置,该位置包含原始字符串和附加部分。

请参阅MSDN字符串参考 。

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

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

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

其次,你可以用以下几行来解决这个问题:

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

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

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

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

资料来源: 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 :通过引用比较对象。

注意2 :typeof(int).Name由reflection方法计算,因此在编译时不会对其进行求值。 这些比较是在编译时进行的。

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

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

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