话不多说,直接走起
一、String
小写:string
大写:String
1.string类是C#中的类,而String是.net framework中的类
2.如果写的是string,在编译的时候编译器会把他转换为String。所以直接使用String会让编译器少做工作,可以提高一下性能。
3.使用string,如果是C#编译的话,比较规范。
4.在c#中string是关键字,而String不是。
5.使用String必须应用命名空间。
6.string表示Unicode字符的字符串
二、string
举例说明:
c# ==》string a="aaa";
如上编码中,string a 表示新建一个string对象,已知string为引用类型,其存储数据的过程为:现在托管堆里开辟内存生成“aaa”,string a则把指针指向“aaa”对应的首地址;
c# ==》a=a+"ccc";
如上编码中,新生成两个对象分别是第一个a和“ccc”,过程为:先在托管堆里开辟内存创建“ccc”,后开辟内存创建第一个a,并把第二个a的值和对象“ccc”的值放入其中,最后把string a的指向指到最新的a对应的首地址。而之前的a和“aaa”对象则在托管堆中,等待GC的回收。
通过以上步骤得出的结论:
1.string对象的不可变性(string对象一经生成,在托管堆中的信息是不变的,改变的只是指针的指向)
2.堆string对象进行操作比较消耗性能,“+”操作就要多生成两个对象
3.当string达到一定长度时(好像是8000),数据会进入大托管堆,此时对对象进行操作时性能损耗会很大,因为他要在托管堆上开辟出一块足够大的内存来存放数据,若是找不到这样的位置,自然就报错了。另外,GC在进行内存回收时,针对小托管堆的回收频率肯定要比大托管堆的频率要高很多,也就是说GC对大托管堆的回收成本很高,也就形成了内存应用不合理的局面。
4.最大长度不超过int的最大值:2147483647(string.length是int型)
5.线程安全
三、stringbuider
stringbuider分为两部分:
首先是.Net2.0版本:
stringbuider a=new stringbuider("aaa");
首先先在托管堆默认开辟默认初始容量16的内存生成“aaa”,并把A指向他
a.append("bbbbb");
若“aaa”+"bbbbbb"长度小于“aaa”容量,则直接添加进去,若“aaa”+"bbbbbb"长度大于“aaa”容量且小于“aaa”容量的2倍,则开辟2*“aaa”容量,若“aaa”+"bbbbbb"长度大于“aaa”容量的两倍,则开辟“aaa”+"bbbbbb"长度的容量,后者一次类推。(我们设两字符长度分别为a、b,若a+b<a.容量,直接添加;若a.容量<a+b<2*a.容量,开辟2*a.容量内存;若2*a.容量<a+b,开辟a+b容量内存)。开辟内存过后,将aaa复制到该内存并追加上”bbbbbb“,完成后将a指针指向该内存,旧的a则在堆中等待GC回收
以上步骤总结:
1.对象不可变形,和string类似都是改指针地址,旧内存等待回收
2.每次append之生成1个对象,也会出现和string相同的问题,当进入大托管堆是很耗性能且会有内存分配错误的问题
3.存在额外的性能消耗(信息拷贝,内存分配)
.Net4.0版本
stringbuider a=new stringbuider("aaa");
首先先在托管堆默认开辟默认初始容量16的内存生成“aaa”,并把A指向他
a.append("bbbbb");
若设两字符长度分别为a、b,若a+b<a.容量,直接进行追加操作,若a.容量<a+b<8000-a.长度,开辟a+b-a.长度(“a+b-a.长度”按照.net2.0规则进行内存容量计算)内存容量,并把a.容量末尾指针指向新开辟内存地址,形成链表,若a+b-a.长度>8000,则分别开辟8000长度内存和a+b-a.长度-8000长度内存,并将其首尾相连形成链表。
总结:
1.首次分配容量默认为16长度
2.相比较.Net2.0,性能更高(缺少了复制原对象内存的过程)
3.始终在小托管堆上,GC效率更高
4.因为是链表的方式,且容量扩展时开辟的内存比较小,所以出现找不到区域开辟内存的几率很小
5.默认最大容量是Int32.MaxValue
6.非线程安全
ok,讲解到此为止!!!如有疑问或者错误,请大家提出和指正~~~