Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#字符串插值是如何编译的?_C#_String Formatting_Roslyn_C# 6.0 - Fatal编程技术网

C#字符串插值是如何编译的?

C#字符串插值是如何编译的?,c#,string-formatting,roslyn,c#-6.0,C#,String Formatting,Roslyn,C# 6.0,我知道插值是string.Format()的语法糖,但是当它与字符串格式化方法一起使用时,它是否有任何特殊的行为/识别 如果我有一个方法: void Print(string format, params object[] parameters) 使用插值对其进行以下调用: Print($"{foo} {bar}"); 以下哪一行调用最等同于字符串插值的编译结果 Print(string.Format("{0} {1}", new[] { foo, bar })); Print("{0} {

我知道插值是
string.Format()
的语法糖,但是当它与字符串格式化方法一起使用时,它是否有任何特殊的行为/识别

如果我有一个方法:

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);
Test(FormattableStringFactory.Create("now is {0}", DateTime.Now));

这里没有什么神奇的

另一方面,如果您在需要
FormattableString
(在.NET 4.6中是一种新类型)的地方使用它,它将被编译为对以下内容的调用:

那里的电话变成了这样:

string s = string.Format("now is {0}", DateTime.Now);
Test(FormattableStringFactory.Create("now is {0}", DateTime.Now));

所以本质上,要回答你的最后一个问题:

此电话:

Print($"{foo} {bar}");
将被翻译为:

Print(string.Format("{0} {1}", foo, bar));
这将导致通过
string.Format
进行格式化的成本,甚至在调用
Print
之前

如果您可以添加或找到一个重载的
Print
,该重载使用
FormattableString
,那么您可以将
string.Format
的实际成本推迟到您确定是否需要登录之后。很难说这在运行时是否有可测量的差异


奖金回合 不仅实际的格式化延迟,而且
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")));
}

我知道一个很容易找到的方法。尝试这两种方法并比较生成的IL。您可以使用LinqPad更快地查看IL代码。或者任何反编译工具,如JustDecomplie或Reflector,您的方法从根本上说是不安全的。如果我用一个正好包含大括号的字符串来调用它呢?@SLaks它并不比
string.Format
更安全或不安全?只要它是以类似的方式记录的,我这里就没有问题了?@JamesThorpe:
String.Format
也有同样的问题,但人们希望不会这样做。而
Print($“{foo}{bar}”)
已中断。因此,本质上,为了回答OPs性能查询,它会导致构造一个新类(
ConcreteFormattableString
,from),但日志框架将继续不计算参数,除非它调用
ToString
方法,如果日志记录方法接受
FormattableString
,那么它将结束日志记录。如果日志框架接受
字符串
,它将通过调用站点上的
string.Format
进行格式化,然后日志框架才能确定是否需要日志。是的,抱歉,我跳过了前面的内容-我已经想到用
FormattableString
:)替换日志包装中的所有
String
参数。请注意,添加接受
FormattableString
的重载不会有帮助,因为
String
在重载解析过程中具有更高的优先级。您必须使用一个单独的方法名。实际上,在某些情况下(例如,当使用
int
时),字符串插值可以编译为字符串串联<代码>var a=“你好”;var b=$“{a}世界”编译为字符串连接<代码>var a=“你好”;var b=$“{a}世界{1}”编译为字符串格式。