C#中string的一些特性

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

  string具有以下的一些特性: 

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

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

代码
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)中搜索是否存在需要的字符串,若有则直接返回,否则创建一个新的副本到拘留池中。

代码
 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 ([0string 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 }


 

 

 

 

  

 

 

posted @ 2010-03-14 11:38  AaronBao  阅读(1526)  评论(0编辑  收藏  举报