System.String 是 C# 基础类型中唯一的引用类型。但是,它却具有很多值类型的特点。
我们来看一段简单的代码:
string text = "Red"; |
按照引用的理论,此处 _tempStr 变量应该是存储的 text 变量的地址,那么修改 _tempStr 变量的值,text 的值就应该随之改变。
那么,此时 text 变量的值应该就是 "Blue",但事实上经过测试 text 变量的值还是 "Red"。
那就说明 _tempStr 变量肯定不是存储的 text 变量的地址。但,这样又违背了它是引用类型的这一特点,那它的内部究竟是怎么样处理的呢?
据我了解,微软应该是在 String 类型中引入了 Copy-On-Write(写时拷贝) 技术,先来简要说明一下什么是 Copy-On-Write 技术:
简单来说,在复制一个对象时并不是真的在内存中把原来对象的数据复制一份到另外一个地址,而是在新对象的内存映射表中指向同原对象相同的位置,并且把那块内存的 Copy-On-Write 位设为 1。在对这个对象执行读操作的时候,内存数据没有变动,直接执行就可以。在写的时候,才真正将原始对象复制一份到新的地址,修改新对象的内存映射表到这个新的位置,然后往这里写。
有一定经验的程序员应该都知道,Copy-On-Write(写时拷贝) 技术使用了 "引用计数" 方式,会有一个变量用于保存引用的数量。
当第一个类构造时,String 的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加。
当有类析构时,这个计数会减一,直到最后一个类析构时,此时的引用计数为 1 或是 0,此时,程序才会真正的释放这块从堆上分配的内存。
说白了,"引用计数" 就是 String 类中写时拷贝的原理!
事实上,String 还是一个不可变的数据类型,一旦对 String 类型的对象进行了初始化,该字符串对象就不能改变了。
为了说明这一点,我们再来看一小段很简单的代码:
string name = "XXXXXX"; |
在执行这段代码时,首先创建一个名为 name 的 String 类型的对象,并初始化为 "XXXXXX"。
此时,.NET 运行库会为该字符串分配足够的内存来保存这个文本,然后,再设置变量 name,来表示这个字符串实例。
从语法上看,第二行代码是将更多的文本添加到此字符串中。在我初学 C# 语言的时候,我也是这么理解的。
实际上却并非如此,而是创建一个新的字符串实例,给他分配足够的内存,然后存储合并的所有文本。