[英]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.