Loading

.NET 中String 和StringBuilder 以及他们的区别

 当在一个循环中将许多字符串连接在一起时,使用 StringBuilder 类可以提升性能,为什么呢?最大区别在于他们的内存分配机制不同。

内存分配:

String

String 对象是不可改变的。每次使用 String 类中的方法之一或进行运算时(如赋值、拼接等)时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间

StringBuilder

StringBuilder 实例的 int Capacity 属性,它表示内存中为存储字符串而物理分配的字符串总数。该数字为当前实例的容量。当字符串操作的结果大于当前的容量时,该属性的值会自动增长。增长的幅度为当前数值的2倍。

注: .NET Framework中可变集合类如ArrayList 的Capacity 属性也类似这种自动分配机制

OK 既然说到String 顺便介绍下String对象的引用比较问题

猜猜下面这段代码将会输出什么结果??

     public void Test1()
        {
            
string a = "abcd";
            
string b = "abcd";
            
/**
             * object.ReferenceEquals(object objA, object objB);
             * 返回结果:
             * 如果 objA 是与 objB 相同的实例,或者如果二者都为空引用,则为 true;否则为 false。
             * **
*/
            Console.WriteLine(
object.ReferenceEquals(a,b));
        }

这段代码将输出

true

是的不信你可以自己验证一下

MSDN的介绍:公共语言运行时通过维护一个表来存放字符串,该表称为“暂存池”,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。 因此,具有特定值的字符串的实例在系统中只有一个

 

再来一段,猜猜这个将会输出什么结果?

  public static void Main(string[] args)
        {             
string a = "abeqwec";
            
string b = "abeqwe" + "c";
            
string c = "abeqwec";
            Console.WriteLine(
object.ReferenceEquals(a,c));
            Console.WriteLine(
object.ReferenceEquals(a,b));
            Console.WriteLine(
object.ReferenceEquals(b,c));
            Console.ReadLine(); 
        }
 
 
IL 代码
.method public hidebysig static void Main(string[] args) cil managed
{
    
.entrypoint
    
.maxstack 2
    
.locals init (
        [
0string a,
        [
1string b,
        [
2string c)
    
L_0000: nop 
    
L_0001: ldstr "abeqwec"
    
L_0006: stloc.0 
    
L_0007: ldstr "abeqwec"
    
L_000c: stloc.1 
    
L_000d: ldstr "abeqwec"
    
L_0012: stloc.2 
    
L_0013: ldloc.0 
    
L_0014: ldloc.2 
    
L_0015: call bool [mscorlib]System.Object::ReferenceEquals(objectobject)
    
L_001a: call void [mscorlib]System.Console::WriteLine(bool)
    
L_001f: nop 
    
L_0020: ldloc.0 
    
L_0021: ldloc.1 
    
L_0022: call bool [mscorlib]System.Object::ReferenceEquals(objectobject)
    
L_0027: call void [mscorlib]System.Console::WriteLine(bool)
    
L_002c: nop 
    
L_002d: ldloc.1 
    
L_002e: ldloc.2 
    
L_002f: call bool [mscorlib]System.Object::ReferenceEquals(objectobject)
    
L_0034: call void [mscorlib]System.Console::WriteLine(bool)
    
L_0039: nop 
    
L_003a: call string [mscorlib]System.Console::ReadLine()
    
L_003f: pop 
    
L_0040: ret 
}

 
由此可见在进行编译为IL代码之前,编译器已经将字符串做了链接,所以对我们的运行结果将不会有影响
以上代码 输出
True 
True 
True
 
针对刚才两条回复提到的字符串驻留 和String.Intern(String) 方法,在MSDN找到了比较详细的介绍
我直接捡主要的贴
 

公共语言运行时通过维护一个表来存放字符串,该表称为“暂存池”,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。 因此,具有特定值的字符串的实例在系统中只有一个。

例如,如果将同一字符串分配给几个变量,运行时就会从暂存池中检索对该字符串的相同引用,并将它分配给各个变量。

Intern 方法使用暂存池来搜索与 str 值相等的字符串。 如果存在这样的字符串,则返回暂存池中它的引用。 如果不存在,则向暂存池添加对 str 的引用,然后返回该引用。

在下面的示例中,值为“MyTest”的字符串 s1 已被拘留,因为它是程序中的文本。 System.Text.StringBuilder 类生成与 s1 同值的新字符串对象。 对该字符串的引用被分配给 s2。 Intern 方法搜索与 s2 具有相同值的字符串。 由于存在此类字符串,因此该方法返回分配给 s1 的引用。 该引用又被分配给 s3。 引用 s1 和 s2 相比较的结果是不相等,因为它们引用了不同的对象;而引用 s1 和 s3 相比较的结果是相等,因为它们引用了同一字符串。

  
string s1 = "MyTest";
string s2 = new StringBuilder().Append("My").Append("Test").ToString();
string s3 = String.Intern(s2);
Console.WriteLine((Object)s2
==(Object)s1); // Different references.
Console.WriteLine((Object)s3==(Object)s1); // The same reference.
 
还有什么不足的希望各位前辈能够指出。
posted @ 2011-05-04 23:05  韩严重  阅读(3626)  评论(20编辑  收藏  举报