到底什么是字符串的不变性?

string 类型是c#中使用最频繁的类型,因此CLR用专门的方法来处理、优化string,使得string虽然你是引用类型,但在表现上被.NET优化为值类型。

先看string的定义是:

  public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>

从这里我们可以得知:

string的本质是字符集合,因此,linq to object 的操作能作用在string上。

string是sealed,该特性是为字符串不变性(恒等性,immutability)和字符串驻留机制提供有效保证。(子类无法继承,因此无法破坏CLR对string的特殊处理机制)

这个特性的表现是,当对实例使用:

Insert(),PadLeft(),Remove(),Replace(),SubString(),ToUpper(),ToLower(),Trim()

等方法时,原有的字符串仍然在内存中,不被改变,对实例操作的结果需要在内存中创建新的字符串对象。

这样做的好处:

1)保证原string对象的稳定性。

2)string不会出现线程同步问题。

这样做的坏处:

性能和内存的双重。

为了应对这个缺点,CLR使用了哈希表类型的暂存池。

 哈希表的key是string,value则是存储托管堆中的地址。当JIT编译方法时,会首先在哈希表中查找每一个字符串常量,如果找不到,则在表中创建一个键值对;如果找到,则将找到的键值对的value值赋给这个对象。

例子:

            string strA = "abc";
            string strB = "abc";
            bool b=ReferenceEquals(strA, strB);//True

这边名strA和strB指向的是同一个地址。

 

两个方法

IsInterned和Intern

根据MSDN对IsInterned的描述:

此方法在暂存池中查找 str。 如果已经将 str 放入暂存池中,则返回对此实例的引用;否则返回 null

例子:

            string s1="abc";    
            string s2=string.IsInterned(s1);
            Console.WriteLine(s2);//"abc"

 很奇怪的一点是:

 string s1=string.IsInterned("def");
            Console.WriteLine(s1); //"def"

预料中结果是null,因为"def"此时并不在暂存表中。结果打印出了"def"。猜测可能是当写"def"时,暂存池中已经将其加入了。

 

            string s1 = "abc";
            string s2 = s1+ "def";
            string s3 = string.IsInterned(s2);
            Console.WriteLine(s3);   //null

 

            string s4 = "abcdef";
            string s1 = "abc";
            string s2 = s1+ "def";
            string s3 = string.IsInterned(s2);
            Console.WriteLine(s3); //"abcdef"

 

而Intern的意思相似:

如果暂存了 str,则返回系统对其的引用;否则返回对值为 str 的字符串的新引用。

区别是:Intern如果在暂存池中查找不到该str时,则将该str添加到暂存池中,而IsInterned则不添加。

例子:

            string strA = "abcdef";
            string strB = "abc";
            string strC = strB + "def";
            var b1 = ReferenceEquals(strA, strC); //False,因为strC是动态构造的,因此这样的字符串不会被添加到暂存池中维护
            strC = string.Intern(strC);  //由于strC不在暂存池中,则将其添加进去。
            var b2 = ReferenceEquals(strA, strC);  //True 

 

 

*本文依据《你必须知道的.NET》一书总结

posted @ 2013-09-26 17:43  瓜王  阅读(1740)  评论(0编辑  收藏  举报