2011-01-21 50 views
6
A = string.Concat("abc","def") 

B = "abc" + "def" 

A對乙在c#中添加字符串,編譯器如何執行它?

最近我一直在困惑,爲什麼很多人會說,相比B.絕對是做了更快的處理,但是,事情是,他們只會說,因爲有人這麼說,或者因爲它就是這樣。我想我可以從這裏聽到更好的解釋。

編譯器如何處理這些字符串?

謝謝!

+2

對於這種大小的字符串,無關緊要 – 2011-01-21 10:44:49

回答

12

我加入C#編譯器團隊時做的第一件事是我重寫了字符串連接的優化器。美好時光。

如前所述,常量字符串的字符串concats是在編譯時完成。非常量字符串做一些花哨的東西:

a + b --> String.Concat(a, b) 
a + b + c --> String.Concat(a, b, c) 
a + b + c + d --> String.Concat(a, b, c, d) 
a + b + c + d + e --> String.Concat(new String[] { a, b, c, d, e }) 

這些優化的好處是,該String.Concat方法可以看看所有的參數,確定它們的長度的總和,然後做出一個大的字符串,可以掌握所有結果。

這裏有一個有趣的一個。假設你有一個返回字符串的方法M:

s = M() + ""; 

如果M()返回null,那麼結果是空字符串。 (null + empty是空的。)如果M不返回null,那麼結果不會因空字符串的串聯而改變。因此,這實際上是優化的,因爲根本不是對String.Concat的調用!它變成

s = M() ?? "" 

整潔,呃?

5

在C#中,字符串的加法運算符只是String.Concat的語法糖。您可以通過在反射器中打開輸出組件來驗證。

需要注意的另一件事是,如果您的代碼中包含字符串文字(或常量)(如示例中所示),編譯器甚至將其更改爲B = "abcdef"

但是,如果使用String.Concat帶有兩個字符串文字或常量,String.Concat仍然會被調用,跳過優化,因此+操作實際上會更快。

所以,總結一下:

stringA + stringB變得String.Concat(stringA, stringB)
"abc" + "def"成爲"abcdef
String.Concat("abc", "def")保持不變

別的東西,我剛做了嘗試:

在C++/CLI,"abc" + "def" + "ghi」 實際上是翻譯成String.Concat(String.Concat("abc", "def"), "ghi")

+3

不與兩個字符串文字不同:`B`將直接設置爲「abcdef」。 – LukeH 2011-01-21 10:45:50

+0

在發佈後立即添加:) – Botz3000 2011-01-21 10:48:17

1

事實上,B的期間解析編譯時間。你將以B = "abcdef"結束,而對於A,連接被推遲到執行時間。

+1

要添加到此,* not *面對文字時使用`+`將被轉換爲`string.Concat()` – Joey 2011-01-21 10:47:34

1

如果字符串文字,在你的問題,然後分配給B的字符串的連接將在編譯時完成。你的榜樣轉化爲:

string a = string.Concat("abc", "def"); 
string b = "abcdef"; 

如果字符串不是字面值,則編譯器會將+操作轉化爲Concat電話。

所以這...

string x = GetStringFromSomewhere(); 
string y = GetAnotherString(); 

string a = string.Concat(x, y); 
string b = x + y; 

...被翻譯成這個在編譯時:

string x = GetStringFromSomewhere(); 
string y = GetAnotherString(); 

string a = string.Concat(x, y); 
string b = string.Concat(x, y); 
1

在這種特殊情況下,兩者實際上是相同的。編譯器會將第二個變體(使用+運算符的變體)轉換爲第一個變體Concat的調用。

那麼,如果兩個實際包含的字符串變量被連接在一起。

此代碼:

B = "abc" + "def"; 

實際上轉變成這樣,無連接的所有:

B = "abcdef"; 

這是可以做到,因爲加法的結果可以在編譯時計算,所以編譯器會這樣做。

但是,如果你使用的是這樣的:

A = String.Concat(stringVariable1, stringVariable2); 
B = stringVariable1 + stringVariable2; 

然後他們兩個會產生相同的代碼。

但是,我想知道那些「多」的說法,因爲我認爲它有些不同。

我認爲他們說的是字符串連接不好,你應該使用StringBuilder或類似的。

舉例來說,如果你這樣做:

String s = "test"; 
for (int index = 1; index <= 10000; index++) 
    s = s + "test"; 

那麼會發生什麼情況是,通過循環每次迭代中,你將建立一個新的字符串,而讓舊有資格進行垃圾回收。

此外,每個這樣的新字符串都會複製舊字符串的所有內容,這意味着您將移動大量內存。

而下面的代碼:

StringBuilder sb = new StringBuilder("test"); 
for (int index = 1; index <= 10000; index++) 
    sb.Append("test"); 

將改爲使用內部緩衝區,比什麼需要更大一些,以防萬一你需要更多的文字追加到它。當該緩衝區變滿時,將分配一個更大的新緩衝區,並將舊的剩下的一個留給垃圾收集。

所以在使用內存和CPU使用率方面,後來變種好得多。

除此之外,我會盡力避免過分關注「代碼變量X比Y更好」,超出了你已有的經驗。舉例來說,我使用StringBuilder現在只是因爲我知道的情況,但這並不是說所有我利用它來編寫代碼實際需要它。

儘量避免花費時間微優化代碼,直到你知道你有一個瓶頸。那時候,通常先測量,稍後再測量的提示仍然有效。