C#字符串插值是如何编译的?
How is C# string interpolation compiled?
我知道插值是 string.Format()
的语法糖,但它在与字符串格式化方法一起使用时有什么特殊的 behavior/recognition 吗?
如果我有方法:
void Print(string format, params object[] parameters)
并使用插值对其进行以下调用:
Print($"{foo} {bar}");
下面哪一行调用最等价于字符串插值的编译结果?
Print(string.Format("{0} {1}", new[] { foo, bar }));
Print("{0} {1}", new[] { foo, bar });
问题背后的原因:诸如 NLog 之类的日志记录框架通常会推迟字符串格式化,直到它们确定将实际写入日志消息为止。一般来说,我更喜欢字符串插值语法,但我需要知道它是否会导致额外的性能损失。
它以两种方式之一编译。
如果您在需要 string
的地方使用字符串插值表达式,它会被编译为对 string.Format
.
的调用
基本上,这个:
string s = $"now is {DateTime.Now}";
变成这样:
string s = string.Format("now is {0}", DateTime.Now);
See it for yourself in Try Roslyn.
这里没什么神奇的。
现在,另一方面,如果您在需要 FormattableString
(.NET 4.6 中的新类型)的地方使用它,它会被编译成对 FormattableStringFactory.Create
的调用:
public void Test(FormattableString s)
{
}
Test($"now is {DateTime.Now}");
那里的电话变成了这样:
Test(FormattableStringFactory.Create("now is {0}", DateTime.Now));
See it for yourself in Try Roslyn.
因此,从本质上讲,在这里回答您的最后一个问题:
这次通话:
Print($"{foo} {bar}");
将翻译为:
Print(string.Format("{0} {1}", foo, bar));
这将在调用 Print
之前通过 string.Format
产生格式化成本。
如果您可以添加或找到需要 FormattableString
的 Print
重载,那么您可以将 string.Format
的实际成本推迟到您弄清楚之后如果你需要登录。这在运行时是否有可测量的差异很难说。
See it for yourself in Try Roslyn.
奖金回合
不仅延迟了实际的格式化,而且 FormattableString
的 ToString
方法允许您指定 IFormatProvider
.
这意味着您也可以推迟局部转换。
public static void Print(FormattableString s)
{
Console.WriteLine("norwegian: " + s.ToString(CultureInfo.GetCultureInfo("nb-NO")));
Console.WriteLine("us: " + s.ToString(CultureInfo.GetCultureInfo("en-US")));
Console.WriteLine("swedish: " + s.ToString(CultureInfo.GetCultureInfo("sv-SE")));
}
我知道插值是 string.Format()
的语法糖,但它在与字符串格式化方法一起使用时有什么特殊的 behavior/recognition 吗?
如果我有方法:
void Print(string format, params object[] parameters)
并使用插值对其进行以下调用:
Print($"{foo} {bar}");
下面哪一行调用最等价于字符串插值的编译结果?
Print(string.Format("{0} {1}", new[] { foo, bar }));
Print("{0} {1}", new[] { foo, bar });
问题背后的原因:诸如 NLog 之类的日志记录框架通常会推迟字符串格式化,直到它们确定将实际写入日志消息为止。一般来说,我更喜欢字符串插值语法,但我需要知道它是否会导致额外的性能损失。
它以两种方式之一编译。
如果您在需要 string
的地方使用字符串插值表达式,它会被编译为对 string.Format
.
基本上,这个:
string s = $"now is {DateTime.Now}";
变成这样:
string s = string.Format("now is {0}", DateTime.Now);
See it for yourself in Try Roslyn.
这里没什么神奇的。
现在,另一方面,如果您在需要 FormattableString
(.NET 4.6 中的新类型)的地方使用它,它会被编译成对 FormattableStringFactory.Create
的调用:
public void Test(FormattableString s)
{
}
Test($"now is {DateTime.Now}");
那里的电话变成了这样:
Test(FormattableStringFactory.Create("now is {0}", DateTime.Now));
See it for yourself in Try Roslyn.
因此,从本质上讲,在这里回答您的最后一个问题:
这次通话:
Print($"{foo} {bar}");
将翻译为:
Print(string.Format("{0} {1}", foo, bar));
这将在调用 Print
之前通过 string.Format
产生格式化成本。
如果您可以添加或找到需要 FormattableString
的 Print
重载,那么您可以将 string.Format
的实际成本推迟到您弄清楚之后如果你需要登录。这在运行时是否有可测量的差异很难说。
See it for yourself in Try Roslyn.
奖金回合
不仅延迟了实际的格式化,而且 FormattableString
的 ToString
方法允许您指定 IFormatProvider
.
这意味着您也可以推迟局部转换。
public static void Print(FormattableString s)
{
Console.WriteLine("norwegian: " + s.ToString(CultureInfo.GetCultureInfo("nb-NO")));
Console.WriteLine("us: " + s.ToString(CultureInfo.GetCultureInfo("en-US")));
Console.WriteLine("swedish: " + s.ToString(CultureInfo.GetCultureInfo("sv-SE")));
}