C#基础教程(十四) String、StringBuilder、”==,equal,ReferenceEquals“

1.内存分区

(1)栈区:由编译器自动分配释放 ,存放值类型的对象本身,引用类型的引用地址(指针),静态区对象的引用地址(指针),代码中必须就栈的大小有明确的定义。栈区内存无需我们管理,也不受GC管理,栈顶元素使用完毕弹出就会立即释放(由操作系统管理)。
(2)堆区(托管堆):用于存放引用类型对象本身。在c#中由.net平台的垃圾回收机制(GC)管理。栈,堆都属于动态存储区,可以实现动态分配。
(3)静态区及常量区:用于存放静态类,静态成员(静态变量,静态方法),常量的对象本身。由于存在栈内的引用地址都在程序运行开始最先入栈,因此静态区和常量区内的对象的生命周期会持续到程序运行结束时,届时静态区内和常量区内对象才会被释放和回收(编译器自动释放)。所以应限制使用静态类,静态成员(静态变量,静态方法),常量,否则程序负荷高。
(4)代码区:存放函数体内的二进制代码。

3.什么时候用String?什么时候用StringBuilder?

String是引用类型。

(1)String的存储方式很特殊,在c#的内存中,在常量区里会分配一块空间叫做String暂存池(常量池),在某些时候,我们的字符串数据是存储在这个常量池中的,而地址依然是存放在栈中。

 字符串一旦创建就不可修改大小,每次使用System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的String对象相关的系统开销可能会非常昂贵(销毁前面的创建新的字符串,新的地址和值)。如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder类。例如当在一个循环中将许多字符串连接在一起时,使用StringBuilder类可以提升性能。

所以对字符串添加或删除操作不频繁的话,就几个固定的string累加的时候就不需要StringBuilder了,毕竟StringBuilder的初始化也是需要时间的。对字符串添加或删除操作比较频繁的话那就用StringBuilder。

(2)微软在处理字符串的时候用到散列表它是什么呢?简单理解就是当你创建了字符串"china"这个字符串的时候,当你再创建这个字符串的时候,编译器是不会再去开辟新的内存来存储的。它会直接指向第一次创建的地址

4.引用和值比较 

1) ==

——对于值类型,比较的是两个对象的代数值是否相等,比较时会自动进行类型转换(自定义值类型需要重载运算符后才可以使用==比较)
——对于引用类型,比较的是两个对象的引用(string类型比较的是值)

int a = 1;
long b = 1;
Debug.Log(a == b);//true

string s1 = "string";
string s2 = "string";
Debug.Log(s1 == s2);//true
 
StringBuilder sb1 = new StringBuilder("StringBuilder"); StringBuilder sb2 = new StringBuilder("StringBuilder");
Debug.Log(sb1 == sb2);//false

2) Equals

——对于值类型,比较的是两个对象的代数值是否相等,比较时不会自动进行类型转换(自定义值类型需要重载运算符后才可以使用==比较)
——对于引用类型,比较的是两个对象的地址(string类型比较的是值)

int a = 1;
long b = 1;
Debug.Log(a.Equals(b));//false
 
string s1 = "string";
string s2 = "string";
Debug.Log(s1.Equals(s2));//true
 
StringBuilder sb1 = new StringBuilder("StringBuilder");
StringBuilder sb2 = new StringBuilder("StringBuilder");
Debug.Log(sb1.Equals(sb2));//false

3) Object.ReferenceEquals(obj1,obj2)

—对于值类型,返回值总是为false,因为装箱后的引用总是不同的
——对于引用类型,比较的是两个对象的引用(string类型比较的是值)

int a = 1;
long b = 1;
Debug.Log(ReferenceEquals(a, b));//false
 
string s1 = "string";
string s2 = "string";
Debug.Log(ReferenceEquals(s1, s2));//true
 
StringBuilder sb1 = new StringBuilder("StringBuilder");
StringBuilder sb2 = new StringBuilder("StringBuilder");
Debug.Log(ReferenceEquals(sb1, sb2));//false

总结 

我们要知道不是所有字符串都放在常量池当中

存放暂存池:
(1)用字面量值创建String对象,例:String str = "ABCD";
(2)用String.Intern(),例:StringBuilder sb = new StringBuilder("ABCD");string str1 = "ABCD";string str2=string.Intern(sb.ToString);
(3)字符串拼接,例:str1 = "ABCD";str2 = "EFG";str1+str2。
不存放暂存池(存放在堆中):
(1)使用char[].Tostring(),例:str1=ABCD”;char[]charArray = str1.ToArray(); str2 = charArray.ToString();
(2)使用new String(),例:str1 =new String()。

编译其实只是一个扫描过程,进行词法语法检查,代码优化而已。我想你说的"编译时分配内存"很不严谨,只是指“编译时赋初值”,它只是形成一个文本,检查无错误,并没有分配内存空间。编译时是不分配内存的,此时只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确

参考:C# 编译&运行原理_wangyihero8的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值