字符串string可以理解为char[],他是一个引用类型。

字符串创建

操作上类似于int,char等类型,直接进行赋值,string str=“bystander”;虽然string 是个类,但是如果你天真的使用

string str=new string("bystander");

来构造,会导致一个编译错误。因为System.String只提供了数个接受Char*,Char[]类型参数的构造函数。 所以只能像下面这样使用,

Char[] cs={'b','y','e'};
String str=new String(cs);

看出来了吧,很麻烦的。所以一般还是使用第一种。

字符串恒定性

恒定性(Immutability),是指字符串一经创建,就不可改变,这是String最为重要的特性之一。具体来说,就是字符串一旦创建,就会在托管堆上分配一块连续的内存空间,我们对其的任何改变都不会影响到原有的String对象,而是重新创建的新的String对象。类似Insert,Substring,ToUpper都只是创建出了新的临时的字符串,会成为下次垃圾回收的目标。 好处:

  • 保证了对String对象的任何操作不会改变源字符串。
  • 恒定性还意味着操作字符串不会出现线程同步问题
  • 恒定性一定程度上,成就了字符串驻留。

字符串驻留

CLR维护一个表来存放字符串,该表叫做拘留表(或驻留池),他包含程序上以编程方式声明或创建的每一个唯一的字符串的引用,因此具有特定值的实例在系统中只有一个。如果将同一个字符串分配给多个变量,那么CLR就会向拘留池检索相同引用,并分配给变量。

通过下面这个例子来说明:

class StringInterning
{
     public static void Main()
        { 
            string strA = "bystander";
            string strB = "bystander";
            Console.WriteLine(ReferenceEquals(strA,strB));
            string strC = "by";
            string strD = strC+"stander";
            Console.WriteLine(ReferenceEquals(strA,strD));
            strD=String.Intern(strD);
            Console.WriteLine(ReferenceEquals(strA,strD));
        }
}

猜猜答案是什么。。 正确答案是:True,False,True 为什么不是我们通常认为的那样呢。这就是因为字符串驻留了

缘起

String类型的特性恒定性,对字符串的任何操作都只会创建新的字符串,这会导致性能下降,而String又用的很频繁,为此,CLR使用字符串驻留来解决这一问题。为此,CLR内部维护一个哈希表,来管理其创建的大部分string对象。其中Key为string本身,Value为分配给对应的string的内存地址。

细节

string strA = "bystander";

  CLR初始化时,创建一个空的哈希表,当JIT编译方法的时候,会首先在哈希表中查找每一个字符串常量,显然找不到任何"bystander"变量,因此会在托管堆中创建一个新的string对系那个strA,并更新哈希表,Key被赋值为"bystander",Value被赋值为strA的引用.也就是Value内保留了"bystander"在托管堆中的引用地址.

string strB = "bystander";

  接着,JIT根据"bystander"查找哈希表,结果找到了,所以JIT不做操作,只把找到的key对应的Value值赋给了strB对象.因此,第一个输出为true,引用相等.

 string strC = "by";
 string strD = strC+"stander";

  同样,JIT向哈希表中添加了Key为"by",Value为托管堆上"by"的地址.返回strC对象.但是注意,strD不同,JIT不检测,因为strD他是动态生成的.这样的字符串不会被添加到哈希表中进行维护,而是在托管堆中直接分配,所以第二个Console输出False.

  对于第三个,我们首先看看Intern方法和IsInterned方法,对于动态生成的字符串,因为没有添加到CLR维护的哈希表,所以字符串驻留机制对其失效,但是可以手工开启,来实现高效的比较字符串相等.

public static string Intern(string str)
public static string IsInterned(string str)

  两者的机制都是去哈希表中查找是否存在str字符串,找到的话也都返回对str的引用,不同的是当哈希表中没有str的话,IsInterned返回null,而Intern将把这个字符串添加到哈希表,并返回引用.注意,IsInterned返回非null并不代表两个字符串引用了相同的地址. 所以

strD=String.Intern(strD);
Console.WriteLine(ReferenceEquals(strA,strD));

就很好理解了.