进一步了解String
今天看到了你真的了解.NET中的String吗? 的文章,写的很不错,对string不错的说明,但是有几点我想补充一下,一旦你的string在堆中创建后,其在内存中都是以const存在,任何的修改都会使其被重新创建为新的string,而指向以前的string的引用将会指向这个新的string!!
测试1:
看下面的代码:
这个代码很简单,声名一个string s并且赋予"1",这个时候s在CLR的内置池中表示为引用,再来看下面的代码:
这个时候你再看输出的结果:第一次的s="1"的时候,s在内置池中,但是当你修改了s的值之后,它已经不在内置池中!!
如果你需要将s再次放置到内置池中,可以这么做:
测试2:
看下面的测试代码:
相信通过上面的代码和反编译后的结果,大家可以看的很清楚,string如何被分配,而在你重新修改string的时候,是如何工作..既然发现了问题,当然也可以解决问题所在..解决和优化的办法很多,我只简单的列出几种,第一种,使用string[]数组来代替..看下面代码:
看看上面的代码,我想很明白,string[]是如何工作的了吧??可以这么理解:每个数组的子项都是一个被内置的string!!
第二种解决办法是char[],如果你知道你的字符串大小,可以这么写char[] c = new char[4]{'1','2','3','4'};这个做法在C/C++中是很不错的,但是在C#似乎用的不多,而且用起来也比较麻烦.因为它不能想C/C++这样: char[] c = {"1234"};不过我依然在做一个加密/解密类的时候用了char[]!!
最后一种也是最常用的:StringBuilder,既然string有重新分配地址的副作用.所以微软也为我们提供了StringBuilder来解决这个问题..
限于篇幅,StringBuilder我将放在下个篇幅中详细介绍它的实现和技巧,发布后我会在这里做连接..
StringBuilder的实现与技巧
测试1:
看下面的代码:
1
string s = "1";
2
Console.WriteLine( String.IsInterned(s)!=null );//这里输出true

2

这个代码很简单,声名一个string s并且赋予"1",这个时候s在CLR的内置池中表示为引用,再来看下面的代码:
1
string s = "1"; //初始化string
2
Console.WriteLine( String.IsInterned(s)!=null );//这里输出true
3
4
s += "2"; //追加string
5
Console.WriteLine( String.IsInterned(s)!=null );//这里输出false
6

2

3

4

5

6

这个时候你再看输出的结果:第一次的s="1"的时候,s在内置池中,但是当你修改了s的值之后,它已经不在内置池中!!
如果你需要将s再次放置到内置池中,可以这么做:
1
string s = "1"; //初始化string
2
Console.WriteLine( String.IsInterned(s)!=null );//输出true
3
4
s += "2"; //追加string
5
Console.WriteLine( String.IsInterned(s)!=null );//输出false
6
7
String.Intern(s); //重新设置为引用
8
Console.WriteLine( String.IsInterned(s)!=null );//这个时候依然输出为true

2

3

4

5

6

7

8

测试2:
看下面的测试代码:
1
string a = "1";//第一次内置string
2
a += "2";//第二次分配,赋值第一次的1到新的地址中,需重新分配内存
3
a += "3";//第三次分配,赋值前两次的1,2到新的地址中,需重新分配内存
4
a += "4";//第四次分配,赋值前三次的1,2,3到新的地址中,需重新分配内存
5
6
/* 使用IL反编译后看的结果
7
*
8
* .method private hidebysig static void Main(string[] args) cil managed
9
{
10
.entrypoint
11
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
12
// 代码大小 43 (0x2b)
13
.maxstack 2
14
.locals init ([0] string a)
15
IL_0000: ldstr "1"
16
IL_0005: stloc.0
17
IL_0006: ldloc.0
18
IL_0007: ldstr "2"
19
IL_000c: call string [mscorlib]System.String::Concat(string,//注意这里,复制一次
20
string)
21
IL_0011: stloc.0
22
IL_0012: ldloc.0
23
IL_0013: ldstr "3"
24
IL_0018: call string [mscorlib]System.String::Concat(string,//复制二次
25
string)
26
IL_001d: stloc.0
27
IL_001e: ldloc.0
28
IL_001f: ldstr "4"
29
IL_0024: call string [mscorlib]System.String::Concat(string,//复制第三次
30
string)
31
IL_0029: stloc.0
32
IL_002a: ret
33
} // end of method Class1::Main
34
*
35

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

相信通过上面的代码和反编译后的结果,大家可以看的很清楚,string如何被分配,而在你重新修改string的时候,是如何工作..既然发现了问题,当然也可以解决问题所在..解决和优化的办法很多,我只简单的列出几种,第一种,使用string[]数组来代替..看下面代码:
1
string[] Arr1 = new string[4];//声名需要内置4个string
2
Arr1[0] = "1";//内置了1
3
Arr1[1] = "2";//内置了2
4
Arr1[2] = "3";//内置了3
5
Arr1[3] = "4";//内置了4
6
/*数组赋值后,在IL反编译后的表现
7
*
8
*
9
*.method private hidebysig static void Main(string[] args) cil managed
10
{
11
.entrypoint
12
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
13
// 代码大小 40 (0x28)
14
.maxstack 3
15
.locals init ([0] string[] Arr1)
16
IL_0000: ldc.i4.4
17
IL_0001: newarr [mscorlib]System.String
18
IL_0006: stloc.0
19
IL_0007: ldloc.0
20
IL_0008: ldc.i4.0
21
IL_0009: ldstr "1"
22
IL_000e: stelem.ref
23
IL_000f: ldloc.0
24
IL_0010: ldc.i4.1
25
IL_0011: ldstr "2"
26
IL_0016: stelem.ref
27
IL_0017: ldloc.0
28
IL_0018: ldc.i4.2
29
IL_0019: ldstr "3"
30
IL_001e: stelem.ref
31
IL_001f: ldloc.0
32
IL_0020: ldc.i4.3
33
IL_0021: ldstr "4"
34
IL_0026: stelem.ref
35
IL_0027: ret
36
} // end of method Class1::Main
37
*
38
* */

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

看看上面的代码,我想很明白,string[]是如何工作的了吧??可以这么理解:每个数组的子项都是一个被内置的string!!
第二种解决办法是char[],如果你知道你的字符串大小,可以这么写char[] c = new char[4]{'1','2','3','4'};这个做法在C/C++中是很不错的,但是在C#似乎用的不多,而且用起来也比较麻烦.因为它不能想C/C++这样: char[] c = {"1234"};不过我依然在做一个加密/解密类的时候用了char[]!!
最后一种也是最常用的:StringBuilder,既然string有重新分配地址的副作用.所以微软也为我们提供了StringBuilder来解决这个问题..
限于篇幅,StringBuilder我将放在下个篇幅中详细介绍它的实现和技巧,发布后我会在这里做连接..
StringBuilder的实现与技巧
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述