static void Main(string[] args)
{
string s1 = "abc";
string s2 = "abc";
Console.WriteLine(ReferenceEquals(s1,s2));
string s3 = "a";
string s4 = "bc";
string s5 = s3 + s4;
Console.WriteLine(ReferenceEquals(s1,s5));
string s6 = GetStr();
Console.WriteLine(ReferenceEquals(s1,s6));
}
static string GetStr()
{
return "abc";
}
可以猜测一下上面的输出
这其中涉及到了字符串驻留的概念,MSDN对于字符串驻留的定义:
CLR通过维护一个表来存放字符串,该表成为驻留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此具有特定值的字符串的实例在系统中只有一个。如果将同一个字符串分配给几个变量没,CLR就会从驻留池中检索对该字符串的相同引用,并将它分配给各个变量
说的很多,通俗解释就是
托管堆中专门有一个表是来存字符串的,如果你新建的字符串在里面有就直接指向该字符串,没有就新建。但是有一个特殊的地方,新建字符串指的是显示的字符串,例如上面的 string s1 = "abc"; 这种的显示的字符串才会被存入到表中。而像 string s5 = s3+s4;这种动态的字符串是不会存到表里面,所以尽管他们的内容相同,但是引用却不一样。
但是string有一个静态方法 Intern ,下面是方法的解释,可以查询是否在 驻留池里,如果不在则添加并返回引用
//
// 摘要:
// 检索系统对指定 System.String 的引用。
//
// 参数:
// str:
// 要在暂存池中搜索的字符串。
//
// 返回结果:
// 如果暂存了 str,则返回系统对其的引用;否则返回对值为 str 的字符串的新引用。
//
// 异常:
// T:System.ArgumentNullException:
// str 为 null。
[SecuritySafeCritical]
public static String Intern(String str);
string.IsIntern,仅仅判断是否在驻留池中
//
// 摘要:
// 检索对指定 System.String 的引用。
//
// 参数:
// str:
// 要在暂存池中搜索的字符串。
//
// 返回结果:
// 如果 str 在公共语言运行时的暂存池中,则返回对它的引用;否则返回 null。
//
// 异常:
// T:System.ArgumentNullException:
// str 为 null。
[SecuritySafeCritical]
public static String IsInterned(String str);
还有一个需要注意的:
这两个的引用是相同的
在IL代码里面直接将 "a"+"bc" 简化成了"abc"
另外,字符串并不会受GC影响,它的驻留是进程级的,也就是说只有将当前的应用程序关闭后才会从内存销毁
String 和 StringBuilder的区别