转载:进一步了解String

本文探讨了C#中String的工作原理及优化方法,包括String的不可变性、内存分配方式,以及使用string[]数组、char[]和StringBuilder进行优化的具体实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天看到了你真的了解.NET中的String吗? 的文章,写的很不错,对string不错的说明,但是有几点我想补充一下,一旦你的string在堆中创建后,其在内存中都是以const存在,任何的修改都会使其被重新创建为新的string,而指向以前的string的引用将会指向这个新的string!!

测试1:
看下面的代码:
1 None.gif string  s  =   " 1 " ;
2 None.gifConsole.WriteLine( String.IsInterned(s) != null  );//这里输出true

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

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

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

相信通过上面的代码和反编译后的结果,大家可以看的很清楚,string如何被分配,而在你重新修改string的时候,是如何工作..既然发现了问题,当然也可以解决问题所在..解决和优化的办法很多,我只简单的列出几种,第一种,使用string[]数组来代替..看下面代码:

 1 None.gif string [] Arr1  =   new   string [ 4 ]; // 声名需要内置4个string
 2 None.gif Arr1[ 0 =   " 1 " ; // 内置了1
 3 None.gif Arr1[ 1 =   " 2 " ; // 内置了2
 4 None.gif Arr1[ 2 =   " 3 " ; // 内置了3
 5 None.gif Arr1[ 3 =   " 4 " ; // 内置了4
 6 ExpandedBlockStart.gifContractedBlock.gif /**/ /*数组赋值后,在IL反编译后的表现
 7InBlock.gif * 
 8InBlock.gif *
 9InBlock.gif *.method private hidebysig static void  Main(string[] args) cil managed
10InBlock.gif{
11InBlock.gif  .entrypoint
12InBlock.gif  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
13InBlock.gif  // 代码大小       40 (0x28)
14InBlock.gif  .maxstack  3
15InBlock.gif  .locals init ([0] string[] Arr1)
16InBlock.gif  IL_0000:  ldc.i4.4
17InBlock.gif  IL_0001:  newarr     [mscorlib]System.String
18InBlock.gif  IL_0006:  stloc.0
19InBlock.gif  IL_0007:  ldloc.0
20InBlock.gif  IL_0008:  ldc.i4.0
21InBlock.gif  IL_0009:  ldstr      "1"
22InBlock.gif  IL_000e:  stelem.ref
23InBlock.gif  IL_000f:  ldloc.0
24InBlock.gif  IL_0010:  ldc.i4.1
25InBlock.gif  IL_0011:  ldstr      "2"
26InBlock.gif  IL_0016:  stelem.ref
27InBlock.gif  IL_0017:  ldloc.0
28InBlock.gif  IL_0018:  ldc.i4.2
29InBlock.gif  IL_0019:  ldstr      "3"
30InBlock.gif  IL_001e:  stelem.ref
31InBlock.gif  IL_001f:  ldloc.0
32InBlock.gif  IL_0020:  ldc.i4.3
33InBlock.gif  IL_0021:  ldstr      "4"
34InBlock.gif  IL_0026:  stelem.ref
35InBlock.gif  IL_0027:  ret
36InBlock.gif} // end of method Class1::Main
37InBlock.gif * 
38ExpandedBlockEnd.gif * */

看看上面的代码,我想很明白,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我将放在下个篇幅中详细介绍它的实现和技巧,发布后我会在这里做连接..

转载于:https://www.cnblogs.com/ufo0303/archive/2006/09/22/511731.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值