[英]When does the C# compiler concatenate interpolated strings?
我們知道C# 優化了字符串文字的連接。 對於那些不知道的人,那是 C# 在內部將其變為:
string myString = "one" + "two" + "three";
進入這個:
string myString = "onetwothree";
我今天的問題是:編譯器如何處理插值字符串? 例如
string myFunc()
{
return "five six seven";
}
string mystring = "Onetwothree";
int myVal = 7;
string test = "eight" +
$"four {mystring} five {myVal} {myFunc()} seven" +
$"six {mystring}";
編輯:這個問題是由以下基本問題提示的:
我正在制作一個 function,它采用 POCO 並在 CSV 中輸出新行。 如何在不犧牲性能的情況下使以下代碼不滑出屏幕邊緣?
public override void Append(MyCustomObject obj)
{
var line = obj.Process();
if (__file is null)
throw new FileNotFoundException("No file open for appending!");
string outstr = $@"""{obj.CogType}"",""{CognosObject.process_xpath(obj.SearchPath)}"",""{obj.DefaultName}"",""{CognosObject.process_pathurl(obj.PathUrl)}"",""{obj.Notes}""";
lock (__file)
__file.Write(Encoding.UTF8.GetBytes(outstr));
}
據我所知,對其進行任何形式的技巧都會增加運行時開銷。
C# 會根據您插值的具體內容做不同的事情。 讓我們看一個簡化的例子:
string mystring = "Onetwothree";
string test = "eight"+
$"four five {mystring} seven"+
$"six {mystring}";
(根據 LINQPad)編譯器將其寫為:
string mystring = "Onetwothree";
string test = string.Concat ("eightfour five ", mystring, " sevensix ", mystring);
在這種情況下,編譯器認識到我所做的只是變量字符串連接,因此它結合了不變部分並將 rest 修改為對String.Concat(string, string, string, string)
的調用。
好的,那么問題中的示例怎么樣? 那么編譯器在這里做了一些不同的事情。 讓我們看一下編譯器生成的代碼(同樣,使用 LINQPad 構建):
string mystring = "Onetwothree";
int myVal = 7;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler (18, 3);
defaultInterpolatedStringHandler.AppendLiteral ("four ");
defaultInterpolatedStringHandler.AppendFormatted (mystring);
defaultInterpolatedStringHandler.AppendLiteral (" five ");
defaultInterpolatedStringHandler.AppendFormatted (myVal);
defaultInterpolatedStringHandler.AppendLiteral (" ");
defaultInterpolatedStringHandler.AppendFormatted (<Main>g__myFunc|4_0 ());
defaultInterpolatedStringHandler.AppendLiteral (" seven");
string test = string.Concat ("eight", defaultInterpolatedStringHandler.ToStringAndClear (), "six ", mystring);
這一次,它使用了DefaultInterpolatedStringHandler
。 本質上它會自動組合插入的字符串並使用這個結構來完成繁重的工作,然后在調用 string.Concat() 期間刷新它。
好的,那么一個稍微復雜一點的例子怎么樣?
string myFunc()
{
return "five six seven";
}
string mystring = "Onetwothree";
int myVal = 7;
string test = "eight" +
$"four {mystring} five {myVal}"+$"beep {myFunc()} {myVal} seven" +
$"six {mystring}";
這里的顯着區別是我現在連接兩個“真實”插值字符串(與字符串"six {mystring}"
相反,它變成字符串連接。在后端?
string mystring = "Onetwothree";
int myVal = 7;
string[] obj = new string[5] { "eight", null, null, null, null };
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler (11, 2);
defaultInterpolatedStringHandler.AppendLiteral ("four ");
defaultInterpolatedStringHandler.AppendFormatted (mystring);
defaultInterpolatedStringHandler.AppendLiteral (" five ");
defaultInterpolatedStringHandler.AppendFormatted (myVal);
obj [1] = defaultInterpolatedStringHandler.ToStringAndClear ();
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler (11, 2);
defaultInterpolatedStringHandler.AppendLiteral ("beep ");
defaultInterpolatedStringHandler.AppendFormatted (<Main>g__myFunc|4_0 ());
defaultInterpolatedStringHandler.AppendLiteral (" ");
defaultInterpolatedStringHandler.AppendFormatted (myVal);
defaultInterpolatedStringHandler.AppendLiteral ("seven");
obj [2] = defaultInterpolatedStringHandler.ToStringAndClear ();
obj [3] = "six ";
obj [4] = mystring;
string test = string.Concat (obj);
一方面,沒有對string.Concat(string, string, string, string, string, string)
的調用,因此將其轉換為對string.Concat(string[]
的調用。這是有道理的。
但請注意! 它創建了兩個DISH 結構。 編譯器甚至沒有嘗試減少它們。 在 x64 本機代碼中, ret
位於 L0210,如果我手動組合插入的字符串,它會跳轉到 L0186,直接節省約 138 個字節,盡管我想對ToStringAndClear
和 DISH 的 ctor 的調用也涉及性能受到打擊。
TLDR:
簡單的內插字符串成為對 string.Concat 的調用。 更復雜的事情不一定以最佳方式運行,盡管您應該意識到這一點,但除非它在您的內部循環中,否則它可能在性能方面並不重要。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.