簡體   English   中英

在c#中添加字符串,編譯器如何做到這一點?

[英]addition of strings in c#, how the compiler does it?

A = string.Concat("abc","def") 

B = "abc" + "def"

A與B.

最近我很困惑為什么很多人會說,與B相比,A的處理速度要快得多。但是,他們只會說,因為有人這么說或者因為它就是這樣。 我想我可以從這里聽到更好的解釋。

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

謝謝!

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

如前所述,常量字符串的字符串連接在編譯時完成。 非常量字符串做一些奇特的東西:

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,連接將推遲到執行時間。

如果字符串是文字,就像你的問題一樣,那么分配給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只是因為我知道這種情況,但這並不是說我編寫的所有使用它的代碼實際上都需要它。

盡量避免花時間微優化您的代碼,直到您知道自己有瓶頸為止。 那個時候,關於措施的通常提示,后來削減,仍然有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM