StringBuilder、String、string三者的区别
先谈谈StringBuilder与String的区别
System.String具备不可修改性,在程序中这样的特性容易产生性能问题。针对这一问题,.NET提供了StringBuilder类可以解决类似的问题
String与StringBuilder均可以对字符串进行处理,它们具备不同的特点,下面将分别进行说明。
String具备以下4个特点:
1.String为引用类型,在堆上分配内存。
2.String对象一旦生成,无法进行修改,如果进行修改操作(并非真正意义上的修改),则会在堆上产生新的对象,同时栈上的引用将指向新的对象。
3.String对象提供了大量字符串操作函数。
4.String运算时将产生一个新的实例。
StringBuilder具备以下2个特点:
1.StringBuilder对象为动态对象 ,允许扩充它所封装的字符串中的字符数量。
2.StringBuilder对象允许修改对象中的内容。
由于String对象一旦生成就无法修改,所以每次使用System.String类中的方法时,都需要在内存中创建一个新的字符串对象,这会分配新的内存给该字符串。如果只是修改字符串,则不需要创建新的对象,则可以使用 StringBuilder。StringBuilder与String相比,它会保留自己的字符串缓冲区。在针对StringBuilder对象执行字符串操作时,会首先检查缓冲区的大小,看其是否能容纳新的字符串,如果不够,则会增加需要的内存。
关于System.String的类型往往被一些程序员所误解,System.String是继承自System.Object的一个子类,所有直接或者间接继承自System.Valuetype的类型属于值类型,这是判断值类型与引用类型的重要依据,根据这个依据,我们不难看出,System.String属于引用类型而不属于值类型。通常String的赋值操作会让程序员其为值类型的错觉,一下实例代码展示了这一特性。
class MyString
{
static void Main()
{
String str_a = "StringObject";
String str_b = str_a;
Console.WriteLine(Object.ReferenceEquals(str_a,str_b));
str_b = "newStringObject"; //从新给str_b赋值
Console.WriteLine(str_a);
Console.WriteLine(str_b);
Console.WriteLine(Object.ReferenceEquals(str_a, str_b));
Console.ReadLine();
}
}
运行结果: true
StringObject
newStringObject
false
这里有一函数需要说明一下Object.ReferenceEquals(object objA,object objB),这个函数的作用是比较两个引用是否指向相同的实例。
程序的开始定义了两个字符串引用str_a,str_b,并且使它们指向了同一个对象实例,调用Object.ReferenceEquals(object objA,object objB)方法验证了这一点,然后修改了str_b的内容,分别查看str_a,str_b的对象,发现它们指向了不同的对象实例,调用Object.ReferenceEquals(object objA,object objB)方法发现两个引用确实不再指向同一实例。下面是关于这个程序的图解分析:
从图中可以看出,str_a,str_b指向堆上的同一个对象,当从新给str_b赋值以后,堆上的内存结构发生了变化,请看下面一幅图
由图中可以看出,当给str_b从新赋值的时候,在堆上又生成了一个对象,并且str_b的引用指向了新的对象。
下面我想谈谈为什么System.String具备不可修改性。
先来看一段代码:
class Test
{
static void Main()
{
String s1="hello";
String s2="hello";
Console.WriteLine(Object.ReferenceEquals(s1, s2));
}
}
结果:true
为什么结果会是true了,
按照我们的常规思维,s1和s2应该会在堆上分配两块不同的内存,但是如果系统里面有很多这样的hello字符串,必然会导致系统内存的大量重复,在传统的系统里面,字符串的重复量一般都很大,所以为了优化这些字符串,微软设计了一种特殊的机制,让两个引用指向同一个对象,这样就减少了内存的重复,这就是字符串的驻留优化。这里我们回到String的不可修改性,假设我们对s1进行修改,那么s2也会被修改掉,那s2肯定会说,你修改我们共享的对象,你..你经过我的同意了吗!!!,那s1就会说,那好,既然你不同意,那我就再创建一个对象,不和你共享了,哼!!!。那微软为了解决这个问题,想出了一个办法,就是不允许修改。但是当我们修改s1的时候明明是可以的呀,我想说一下这里所谓的修改并不是真正意义上的修改,而是在堆上又重新创建了一个对象,让其指针重新指向新的对象,而原来的对象并没有发生任何的变化,因为原来的对象可能还有其它的几个引用在共享这个对象。
下面我再说说关于字符串的驻留优化机制是如何实现的:
当我们创建一个string对象的时候,JIT编译器会将该字符串的值写进一张哈希表里面,如果再有新的字符串对象被创建,就会与哈希表里面的值进行对比,如果你所创建的对象已经存在与哈希表内,就把该指针指向表里面的那个对象,不会再新建对象,这样就实现了共享。
下面我想说说在使用字符串操作函数需要注意的一个问题
如果使用的函数会对对象进行修改,就需要对指针进行重新的赋值,否则会产生一个没有引用的对象,这里我需要说明一下,一个函数创建了一个堆对象,并不会“因为不返回而不存在”,这个堆对象仍然会生成,只不过会快速失去引用,而成为垃圾对象,下面我举例说明
class Test
{
static void Main()
{
String s1="hello";
//s1.ToUpper(); // 错误,新产生的对象没有被引用
s1=s1.ToUpper(); //正确
}
}
下面让我们再看看String与string的区别:
众所周知,C#语言是区分大小写的,String与string肯定代表不同的对象,但是在C#中使用String或string定义变量并没有多大的区别。这是为什么了?
其实,String是CLR的类型名称,即属于通用类型系统(CTS),而string是C#中的类型名称,在C#编译时,会默认添加几行代码。string是String的一个别名,两者在使用过程中没什么区别,在系统开发的过程中,应尽量使用统一的关键字,避免混用String与string。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架