本文提供了此 API 参考文档的补充说明。
公共语言运行时通过维护一个名为驻留池的表来节省字符串存储,该表包含对程序中声明或通过编程方式创建的每个唯一文本字符串的单一引用。 因此,具有特定值的文字字符串的实例仅在系统中存在一次。 例如,如果将相同的文本字符串分配给多个变量,运行时将从实习生池中检索对文本字符串的相同引用,并将其分配给每个变量。
Intern 方法使用驻留池来搜索等于其参数 str
值的字符串。 如果存在这样的字符串,则返回其在驻留池中的引用。 如果该字符串不存在,则会向实习生池添加对 str
的引用,然后返回该引用。 (相反,如果请求的字符串不存在于实习生池中,该方法 IsInterned(String) 将返回 null 引用。
在下面的例子中,字符串 s1
的值为“MyTest”,因为它是程序中的文字,所以已经被驻留了。 该 System.Text.StringBuilder 类生成一个新的字符串对象,其值与 s1
相同。 将该字符串的引用分配给s2
。 该方法Intern搜索与s2
值相同的字符串。 由于存在此类字符串,因此该方法返回分配给 s1
的同一引用。 然后将该引用分配给 s3
。 引用 s1
和 s2
比较不相等,因为它们引用不同的对象;引用 s1
和 s3
比较相等,因为它们引用了相同的字符串。
string s1 = "MyTest";
string s2 = new StringBuilder().Append("My").Append("Test").ToString();
string s3 = String.Intern(s2);
Console.WriteLine((Object)s2==(Object)s1); // Different references.
Console.WriteLine((Object)s3==(Object)s1); // The same reference.
let s1 = "MyTest"
let s2 = StringBuilder().Append("My").Append("Test").ToString()
let s3 = String.Intern s2
printfn $"{s2 :> obj = s1 :> obj}" // Different references.
printfn $"{s3 :> obj = s1 :> obj}" // The same reference.
Dim s1 As String = "MyTest"
Dim s2 As String = New StringBuilder().Append("My").Append("Test").ToString()
Dim s3 As String = String.Intern(s2)
Console.WriteLine(CObj(s2) Is CObj(s1)) ' Different references.
Console.WriteLine(CObj(s3) Is CObj(s1)) ' The same reference.
性能注意事项
如果您尝试减少应用程序分配的内存总量,请记住,驻留字符串会产生两个不良的副作用。 首先,在公共语言运行库(CLR)终止之前,为驻留 String 对象分配的内存通常不会被释放。 原因是 CLR 对驻留对象 String 的引用可能在您的应用程序甚至应用程序域终止后仍然存在。 其次,要在内存中驻留字符串,必须先创建字符串。 使用String对象的内存仍必须进行分配,即使该内存最终会被垃圾回收。
CompilationRelaxations.NoStringInterning 枚举成员将程序集标记为不需要字符串文字驻留。 可以使用 NoStringInterning 属性将 CompilationRelaxationsAttribute 应用于程序集。 此外,使用 Ngen.exe(本机映像生成器) 在运行时之前编译程序集时,字符串不会在模块之间驻留。