A = string.Concat("abc","def")
B = "abc" + "def"
最近我一直在困惑,爲什麼很多人會說,相比B.絕對是做了更快的處理,但是,事情是,他們只會說,因爲有人這麼說,或者因爲它就是這樣。我想我可以從這裏聽到更好的解釋。
編譯器如何處理這些字符串?
謝謝!
A = string.Concat("abc","def")
B = "abc" + "def"
最近我一直在困惑,爲什麼很多人會說,相比B.絕對是做了更快的處理,但是,事情是,他們只會說,因爲有人這麼說,或者因爲它就是這樣。我想我可以從這裏聽到更好的解釋。
編譯器如何處理這些字符串?
謝謝!
我加入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() ?? ""
整潔,呃?
在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")
事實上,B的期間解析編譯時間。你將以B = "abcdef"
結束,而對於A,連接被推遲到執行時間。
要添加到此,* not *面對文字時使用`+`將被轉換爲`string.Concat()` – Joey 2011-01-21 10:47:34
如果字符串文字,在你的問題,然後分配給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);
在這種特殊情況下,兩者實際上是相同的。編譯器會將第二個變體(使用+
運算符的變體)轉換爲第一個變體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現在只是因爲我知道的情況,但這並不是說所有我利用它來編寫代碼實際需要它。
儘量避免花費時間微優化代碼,直到你知道你有一個瓶頸。那時候,通常先測量,稍後再測量的提示仍然有效。
對於這種大小的字符串,無關緊要 – 2011-01-21 10:44:49