C#:具有相同内容的字符串

我听过并读过一个字符串不能改变(不可变?)。 我想这应该是正确的。 但我也听说过两个具有相同内容的字符串共享相同的内存空间(或者你称之为内存空间)。 它是否正确?

如果是这样,这是否意味着如果我创建一个包含数千个字符串的List,如果这些字符串中的大多数彼此相等,那么根本不会占用太多空间?

编辑:在下面的答案中,我将实习池称为AppDomain特定的; 我很确定这是我之前观察过的,但是String.Intern的MSDN文档表明整个过程都有一个实习池,这使得这一点变得更加重要。

原始答案

(我打算将此添加为评论,但我认为这是一个非常重要的一点,需要额外的答案……)

正如其他人所解释的那样,字符串实习发生在所有字符串文字中,而不是“动态创建”字符串(例如从数据库或文件中读取的字符串,或使用StringBuilderString.Format构建的StringBuilder )。

但是,我建议调用String.Intern来绕过后一点:它会AppDomain的生命周期中填充实习池。 相反,请使用仅限您使用的本地池。 以下是此类池的示例:

 public class StringPool { private readonly Dictionary contents = new Dictionary(); public string Add(string item) { string ret; if (!contents.TryGetValue(item, out ret)) { contents[item] = item; ret = item; } return ret; } } 

然后,您只需使用以下内容:

 string data = pool.Add(ReadItemFromDatabase()); 

(请注意,池不是线程安全的;正常使用不需要它。)

通过这种方式,您可以在不再需要时立即丢弃池,而不是永远在内存中存在大量字符串。 如果你真的想要,你也可以使它更聪明,实现LRU缓存或其他东西。

编辑:只是为了澄清为什么这比使用String.Intern更好…假设你从数据库或日志文件中读取一堆字符串,处理它们,然后转移到另一个任务。 如果在这些字符串上调用String.Intern ,只要您的AppDomain处于活动状态,它们就永远不会被垃圾收集 – 甚至可能不会。 如果您加载了几个不同的日志文件,您将逐渐累积实习池中的字符串,直到您完成或内存不足为止。 相反,我建议这样的模式:

 void ProcessLogFile(string file) { StringPool pool = new StringPool(); // Process the log file using strings in the pool } // The pool can now be garbage collected 

在这里,您可以获得同一文件中多个字符串的好处,这些字符串仅在内存中存在一次(或者至少只能过一次gen0)但您不会污染“全局”资源(实习池)。

这或多或少是真的。 它被称为“字符串实习”。 字符串文字只在内存中出现一次,设置为相同值的每个变量都指向此单个表示。 但是,代码中创建的字符串不会自动实现。

http://msmvps.com/blogs/manoj/archive/2004/01/09/1549.aspx

如果我没记错的话,代码中硬编码的字符串将单独汇总。 这称为“Interned”,并且有一种方法来查询字符串是否为: String.IsInterned Method

在“备注”下的该页面上,您可以阅读:

公共语言运行库自动维护一个名为“intern pool”的表,该表包含程序中声明的每个唯一文字字符串常量的单个实例,以及以编程方式添加的任何唯一String实例。

希望这对你有所帮助,如果我错了,请纠正我。

马蒂亚斯

为了使字符串“共享”它们的内存位置是在实习池中实现它们,它包含对程序中以编程方式声明或创建的每个唯一文字字符串的单个引用。

请注意,代码中的所有字符串文字都会自动实现。