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

本文深入探讨了C#中的String类型,解析了其作为引用类型却被优化为值类型的内部机制,介绍了string的不可变性和字符串驻留机制,并通过实例说明了如何利用Intern和IsInterned方法提高内存效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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》一书总结

转载于:https://www.cnblogs.com/Benjamin/p/3338474.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值