2014-04-15 171 views
4

我知道字符串是C#中不可變的引用類型。但我有一個更具體的問題。修改字符串

string a = "hello "; 
string b = a; 
a += "world"; 
Console.WriteLine(b); 

上面的代碼將產生輸出hello。我意識到這是因爲字符串是不可變的,所以一旦我們創建了一個值爲「hello」的字符串實例,它就不能被修改。但我想知道,當我們做a += "world"會發生什麼?縱觀IL似乎並沒有給我的原委不幸的是(或者它可能是我太新手看到更大的畫面)

IL_0001: ldstr  "hello " 
IL_0006: stloc.0  // a 
IL_0007: ldloc.0  // a 
IL_0008: stloc.1  // b 
IL_0009: ldloc.0  // a 
IL_000A: ldstr  "world" 
IL_000F: call  System.String.Concat 
IL_0014: stloc.0  // a 
IL_0015: ldloc.0  // a 
IL_0016: call  System.Console.WriteLine 

的IL展現給我什麼,我就已經猜到了字符串「hello」與「world」連接成一個新的字符串。但是,這是否意味着這行實際上完成的是:

  1. 創建具有價值的「世界」的字符串對象的新實例。
  2. 讀取我們的第一個字符串對象「hello」的值,然後將該值與「world」連接以形成第三個字符串。
  3. 將上述字符串的引用返回到a
  4. 「世界」字符串實例現在孤立並標記爲收集?

還是我完全錯了?我真的想知道「幕後」會發生什麼事情,或者說..

+1

不,你根本沒有錯。你基本上自己回答你的問題。這正是發生的情況。 –

回答

1

因爲字符串是不可變的一個等同於你的程序是:

string a = "hello "; 
string b = a; 
a = new string(a + "world"); 

因爲字符串是不可改變的new運營商基本上是包括了你。

如果您有興趣,內部字符串被存儲爲字符數組(see here)。它們實際上存儲在堆中,這就是爲什麼仔細使用字符串很重要(堆內存不是無限的)。我還在這裏發現了這個其他相關的堆棧問題:How are String and Char types stored in memory in .NET?

+1

對,所以我們在這個程序中實際上創建了3個字符串對象的單獨實例?一個原始的(你好),「世界」和一個+「世界」(「你好世界」)? –

+0

@OverlyExcessive是!每當你聲明一個字符串,或者在一個語句中使用一個字符串,或者甚至使用一個字符串操作('+','+ =')時,.NET運行時都會在堆上創建另一個字符串。唯一的例外是編譯器優化和連接字符串作爲編譯的一部分。 –

+0

.NET中的字符串*不是以null結尾的。如果他們是,他們將不能包含「\ 0」字符(他們當然可以)。 – Kyle

1

是的,a += "world";真的是你所困惑的,相當於a = a + "world";

這是一個+=嚴格只是句法糖的情況。不過,誰不喜歡一些甜食?

1

當你寫a += "world"是你寫a = a + "world",這實際上創建一個從字符串a"world"的串聯一個新的字符串,然後將結果向a分配。

0

當您執行a+="World"時,它將創建一個新值爲a + "world"的字符串。由於hello仍被b使用,因此尚未收集垃圾。