C#中string的一些特性

  C#中string类型是一个比较特别的类型,它是一种引用类型,但在使用中,它表现的像一个值类型一样。这是因为string是不可变的(immutable)。

  string具有以下的一些特性: 

  1. string是一个字符序列,是String类的一个别名,别且它是一个关键字。

  2. string是引用类型,每个string实例是一个常量,是不可变的,因此对一个string进行修改时,实际上都是创建了一个新实例。    

ExpandedBlockStart.gif 代码
1           static   void  Main( string [] args)
2          {
3 
4               string  str  =   " First " ;
5               string  str_1  =  str; // str_1与str指向同一个实例
6              Console.WriteLine( string .ReferenceEquals(str, str_1)); // 结果是True
7              str  +=   " New " ; // 修改str的值
8              Console.WriteLine( string .ReferenceEquals(str, str_1)); // 结果是False
9          }

  3. 创建一个string实例时, 首先会从拘留池(Intern pool)中搜索是否存在需要的字符串,若有则直接返回,否则创建一个新的副本到拘留池中。

ExpandedBlockStart.gif 代码
 1           static   void  Main( string [] args)
 2          {
 3               string  str  =   " First " ;
 4              Console.WriteLine( string .IsInterned(str)  !=   null ); // 结果是True
 5 
 6               string  str_1  =   " First " ; // str_1的值与str相同
 7              Console.WriteLine( string .IsInterned(str_1)  !=   null ); // 结果是True
 8 
 9              Console.WriteLine( string .ReferenceEquals(str, str_1)); // 结果是True,str和str_1指向同一个实例
10          }

   4. 编译器会对字符串的操作进行优化。

1           static   void  Main( string [] args)
2          {
3               string  str  =   " First "   +   " Second " ; // 看上去是创建了两个字符串实例
4              Console.WriteLine(str);
5          }

   看上去,好像是先创建了两个字符串实例,实际上编译器已经对这种能明确结果的运算进行了优化,在MSIL中:

 1  .method   private   hidebysig  static  void   Main( string [] args)  cil   managed
 2  {
 3     .entrypoint
 4     //  代码大小       15 (0xf)
 5     .maxstack    1
 6     .locals   init  ([ 0 string  str)
 7     IL_0000:    nop
 8     IL_0001:    ldstr        " FirstSecond "
 9     IL_0006:    stloc.0
10     IL_0007:    ldloc.0
11     IL_0008:    call         void  [mscorlib]System.Console::WriteLine( string )
12     IL_000d:    nop
13     IL_000e:    ret
14  //  end of method Program::Main

  在文章中还有对其他情况进行更详细的实验。

 

  另外,我们会经常判断一个字符串是否为空,使用str.Length == 0速度最快,这点网上有很多比较了,自己证明一下也很容易。使用Reflector查看了string.IsNullOrEmpty()的具体实现后,也的确是用这样的判断方法。

 1  public   static   bool  IsNullOrEmpty( string  value)
 2  {
 3       if  (value  !=   null )
 4      {
 5           return  (value.Length  ==   0 );
 6      }
 7       return   true ;
 8  }


  还有,是关于string.Empty与“”的区别,通过Reflector,能够看到string.Empty是这样定义和初始化的:

 1  public   static   readonly   string  Empty;
 2 
 3  static  String()
 4  {
 5      Empty  =   "" ;
 6      WhitespaceChars  =   new   char [] { 
 7           ' \t ' ' \n ' ' \v ' ' \f ' ' \r ' '   ' ' \x0085 ' ' \x00a0 ' ' ' '   ' ' ' ' ' ' ' ' ' ' ' ' '
 8           ' ' ' ' ' ' ' ' ' ' ' \u2028 ' ' \u2029 ' '   ' '  '
 9       };
10  }

  那么我们这样使用

1           static   void  Main( string [] args)
2          {
3               string  str  =   string .Empty;
4               string  str_1  =   "" ;
5              Console.WriteLine( string .IsInterned(str)  !=   null ); // 结果是True
6              Console.WriteLine( string .IsInterned(str_1)  !=   null ); // 结果是True
7              Console.WriteLine( string .ReferenceEquals(str,str_1)); // 结果是True
8          }

  str和str_1的指向是同一个实例。这说明了string.Empty与""其实是一样的,有点小区别的,就是使用string.Empty的性能要比""稍微好点,因为使用string.Empty则会直接指向这个静态变量值,而直接使用""会有一次搜索拘留池的操作。

 

  最后,来个比较全的例子对string的拘留池以及在内存中创建的string实例进行总结下:

 1  using  System;
 2  using  System.Collections.Generic;
 3  using  System.Linq;
 4  using  System.Text;
 5 
 6  namespace  ConsoleApplication1
 7  {
 8       class  Program
 9      {
10           static   void  Main( string [] args)
11          {
12               string  a  =   new  StringBuilder().Append( ' a ' ).ToString();
13               string  b  =   new  StringBuilder().Append( ' b ' ).ToString();
14               string  c  =   new  StringBuilder().Append( ' c ' ).ToString();
15               string  d  =   new  StringBuilder().Append( ' d ' ).ToString();
16               string  e  =   new  StringBuilder().Append( ' a ' ).Append( ' b ' ).ToString();
17               string  f  =   new  StringBuilder().Append( ' e ' ).Append( ' f ' ).ToString();
18               string  g  =   new  StringBuilder().Append( ' m ' ).Append( ' n ' ).ToString();
19               string  h  =   new  StringBuilder().Append( ' e ' ).ToString();
20               string  m  =   new  StringBuilder().Append( ' m ' ).ToString();
21               string  n  =   new  StringBuilder().Append( ' n ' ).ToString();
22 
23              Console.WriteLine( string .IsInterned(a)  !=   null ); // 结果为False,拘留池中没有储存"a"
24              Console.WriteLine( string .IsInterned(b)  !=   null ); // 结果为True,因为本函数中(下面b1变量)已经有明文字符串"b",则会被添加到拘留池
25              Console.WriteLine( string .IsInterned(c)  !=   null ); // 结果为False,拘留池中还没有储存"c",注意调用Test()后的结果
26              Console.WriteLine( string .IsInterned(d)  !=   null ); // 结果为False,拘留池中还没有储存"d",注意实例化TestClass后的结果
27              Console.WriteLine( string .IsInterned(e)  !=   null ); // 结果为False,拘留池中没有储存"ab"
28              Console.WriteLine( string .IsInterned(f)  !=   null ); // 结果为True,下面f1变量中的"e"+"f"会被优化成"ef",将"ef"添加到拘留池
29              Console.WriteLine( string .IsInterned(g)  !=   null ); // 结果为False,拘留池中没有储存"mn"
30              Console.WriteLine( string .IsInterned(h)  !=   null ); // 结果为False,下面f1变量中的"e"+"f"会被优化成"ef",不会有"e"被添加到拘留池
31              Console.WriteLine( string .IsInterned(m)  !=   null ); // 结果为True,给g1变量初始化时,将"m"添加到拘留池
32              Console.WriteLine( string .IsInterned(n)  !=   null ); // 结果为True,给g1变量初始化时,将"n"添加到拘留池
33 
34              Test();
35              Console.WriteLine( string .IsInterned(c)  !=   null ); // 结果为True,调用的Test()方法中,已经将"c"添加拘留池中
36              TestClass testClass  =   new  TestClass();
37              Console.WriteLine( string .IsInterned(d)  !=   null ); // 结果为True,实例化TestClass时,已经将"d"添加拘留池中
38 
39               string  b1  =   " b " ; // CLR会将当前的方法中所有出现的的明文字符串(优化后的如"x"+"y"则被优化为"xy")添加到拘留池,然后才开始运行,故变量b1虽然定义在后面,但"b"已经被添加到拘留池了
40               string  f1  =   " e "   +   " f "   +  a; // 编译器会优化为"ef"+a,并将"ef"添加到拘留池,不会将"e"和"f"添加到拘留池
41               string  g1  =   " m "   +  a  +   " n " ; // 将"m"和"n"添加到拘留池
42               string  f2  =   " efa " ; // 将"efa"添加到拘留池
43               string  f3  =   " e "   +   " f "   +   " a " ; // 编译器会优化为"efa",f3将指向拘留池的内存
44 
45               // 上述string.IsInterned()方法只是查看变量的值是否在添加到拘留池中,并不表示变量指向拘留池的内存
46               // 事实上非明文字符串的变量都将指向开辟新内存
47 
48              Console.WriteLine( string .ReferenceEquals(b1, b)); // 结果为False,变量b的值"b"在拘留池中已经存在,但b指向的是新开辟内存,b1指向的是拘留池的内存
49              Console.WriteLine( string .ReferenceEquals(b1,  " b " )); // 结果为True
50 
51              Console.WriteLine( string .ReferenceEquals(f1, f2)); // 结果为False,f1指向的是"ef"+a运算时新开辟的内存,f2指向的是拘留池的内存
52              Console.WriteLine( string .ReferenceEquals(f2, f3)); // 结果为True,f3被编译器优化为"efa",因此也指向拘留池的内存
53              Console.WriteLine( string .ReferenceEquals(f3,  " efa " )); // 结果为True
54          }
55 
56           static   void  Test()
57          {
58               string  c1  =   " c " ;
59          }
60      }
61       public   class  TestClass
62      {
63           string  d1  =   " d " ;
64      }
65  }


 

 

 

 

  

 

 

转载于:https://www.cnblogs.com/aaronbao/archive/2010/03/14/1685418.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值