使用
$ 字符将字符串字面量标识为内插字符串。 内插字符串是可能包含内插表达式的字符串文本。 将内插字符串解析为结果字符串时,编译程序会将带有内插表达式的项替换为表达式结果的字符串表示形式。
字符串内插为格式化字符串提供了一种可读性和便捷性更高的方式。 它比字符串复合格式设置更容易阅读。 下面的示例使用了这两种功能生成同样的输出结果:
var name = "Mark";
var date = DateTime.Now;
// Composite formatting:
Console.WriteLine("Hello, {0}! Today is {1}, it's {2:HH:mm} now.", name, date.DayOfWeek, date);
// String interpolation:
Console.WriteLine($"Hello, {name}! Today is {date.DayOfWeek}, it's {date:HH:mm} now.");
// Both calls produce the same output that is similar to:
// Hello, Mark! Today is Wednesday, it's 19:40 now.
可以使用内插字符串初始化 常量 字符串。 仅当内插字符串中的所有内插表达式也是常量字符串时,才可以执行此操作。
内插字符串的结构
若要将字符串标识为内插字符串,可在该字符串前面加上 $ 符号。 字符串字面量开头的 $ 和 " 之间不能有任何空格。
具备内插表达式的项的结构如下所示:
{<interpolationExpression>[,<width>][:<formatString>]}
括号中的元素是可选的。 下表说明了每个元素:
| 元素 | 描述 |
|---|---|
interpolationExpression |
生成需要设置格式的结果的表达式。 当表达式为 null 时,输出为空字符串 (String.Empty)。 |
width |
常数表达式,它的值定义表达式结果的字符串表示形式中的最小字符数。 如果值为正,则字符串表示形式为右对齐;如果值为负,则为左对齐。 有关详细信息,请参阅复合格式设置文章的 Width 组件部分。 |
formatString |
受表达式结果类型支持的格式字符串。 有关详细信息,请参阅复合格式设置一文的格式字符串组件部分。 |
以下示例使用上表中所描述的可选格式设置组件:
Console.WriteLine($"|{"Left",-7}|{"Right",7}|");
const int FieldWidthRightAligned = 20;
Console.WriteLine($"{Math.PI,FieldWidthRightAligned} - default formatting of the pi number");
Console.WriteLine($"{Math.PI,FieldWidthRightAligned:F3} - display only three decimal digits of the pi number");
// Output is:
// |Left | Right|
// 3.14159265358979 - default formatting of the pi number
// 3.142 - display only three decimal digits of the pi number
从 C# 11 开始,可以在内插表达式中使用换行符,以使表达式的代码更具可读性。 下面的示例展示了换行符如何提高涉及模式匹配的表达式的可读性:
string message = $"The usage policy for {safetyScore} is {
safetyScore switch
{
> 90 => "Unlimited usage",
> 80 => "General usage, with daily safety check",
> 70 => "Issues must be addressed within 1 week",
> 50 => "Issues must be addressed within 1 day",
_ => "Issues must be addressed before continued use",
}
}";
内插原始字符串字面量
从 C# 11 开始,可以使用内插原始字符串字面量,如以下示例所示:
int X = 2;
int Y = 3;
var pointMessage = $"""The point "{X}, {Y}" is {Math.Sqrt(X * X + Y * Y):F3} from the origin""";
Console.WriteLine(pointMessage);
// Output is:
// The point "2, 3" is 3.606 from the origin
要在结果字符串中嵌入 { 和 } 字符,请让内插原始字符串字面量以多个 $ 字符开头。 执行此操作时,任何长度短于 { 字符数的 } 或 $ 字符序列都会嵌入到结果字符串中。 若要将任何内插表达式包含在该字符串中,需要使用与 $ 字符数相同的大括号数,如以下示例所示:
int X = 2;
int Y = 3;
var pointMessage = $$"""{The point {{{X}}, {{Y}}} is {{Math.Sqrt(X * X + Y * Y):F3}} from the origin}""";
Console.WriteLine(pointMessage);
// Output is:
// {The point {2, 3} is 3.606 from the origin}
在前面的示例中,内插原始字符串字面量以两个 $ 字符开头。 你需要将每个内插表达式放在双大括号({{ 和 }})之间。 单个大括号嵌入到结果字符串中。 如果需要将重复的 { 或 } 字符嵌入结果字符串中,请使用相应增加的 $ 字符数来指定内插原始字符串字面量。 如果字符串文本的重复大括号数多于 $ 字符数,则 { 和 }字符将从内到外分组。 在前面的示例中,文本 The point {{{X}}, {{Y}}} 将 {{X}} 和 {{Y}} 解释为内插表达式。 输出字符串中完整地包含外部 { 和 }。
小窍门
Visual Studio 和 C# 开发工具包在原始字符串文本包含 JSON 数据或正则表达式时提供一些验证和语法突出显示。
这些工具分析文本。 如果工具确信文本表示 JSON 或正则表达式,编辑器将提供语法着色。
可以通过在声明上方添加指示格式的注释来改善该体验:
-
// lang=json指示原始字符串文本表示 JSON 数据。 -
// lang=regex指示原始字符串文本表示正则表达式。
当原始字符串文本用作参数时,参数使用该 System.Diagnostics.CodeAnalysis.StringSyntaxAttribute 参数来指示格式,这些工具将验证某些格式类型的原始字符串文本。 支持 JSON 和正则表达式。
对于某些格式,注释或属性启用代码建议,为基于格式的字符串文本提供修补程序。
特殊字符
要在内插字符串生成的文本中包含大括号 "{" 或 "}",请使用两个大括号,即 "{{" 或 "}}"。 有关详细信息,请参阅复合格式设置一文的转义括号部分。
由于冒号(“:”)在内插表达式项中具有特殊含义,若要在内插表达式中使用 条件运算符 ,请将该表达式括在括号中。
以下示例演示了如何在结果字符串中包括大括号。 它还演示了如何使用条件运算符:
string name = "Horace";
int age = 34;
Console.WriteLine($"He asked, \"Is your name {name}?\", but didn't wait for a reply :-{{");
Console.WriteLine($"{name} is {age} year{(age == 1 ? "" : "s")} old.");
// Output is:
// He asked, "Is your name Horace?", but didn't wait for a reply :-{
// Horace is 34 years old.
内插逐字字符串以 $ 和 @ 字符开头。 可以按任意顺序使用 $ 和 @:$@"..." 和 @$"..." 均为有效的内插逐字字符串。 有关逐字字符串的详细信息,请参阅字符串和逐字标识符文章。
特定于区域性的格式设置
默认情况下,内插字符串将 CultureInfo.CurrentCulture 属性定义的当前区域性用于所有格式设置操作。
要将插值字符串解析为特定文化的结果字符串,请使用 String.Create(IFormatProvider, DefaultInterpolatedStringHandler) 方法,该方法从 .NET 6 开始提供。 下面的示例演示如何执行此操作:
double speedOfLight = 299792.458;
System.Globalization.CultureInfo.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("nl-NL");
string messageInCurrentCulture = $"The speed of light is {speedOfLight:N3} km/s.";
var specificCulture = System.Globalization.CultureInfo.GetCultureInfo("en-IN");
string messageInSpecificCulture = string.Create(
specificCulture, $"The speed of light is {speedOfLight:N3} km/s.");
string messageInInvariantCulture = string.Create(
System.Globalization.CultureInfo.InvariantCulture, $"The speed of light is {speedOfLight:N3} km/s.");
Console.WriteLine($"{System.Globalization.CultureInfo.CurrentCulture,-10} {messageInCurrentCulture}");
Console.WriteLine($"{specificCulture,-10} {messageInSpecificCulture}");
Console.WriteLine($"{"Invariant",-10} {messageInInvariantCulture}");
// Output is:
// nl-NL The speed of light is 299.792,458 km/s.
// en-IN The speed of light is 2,99,792.458 km/s.
// Invariant The speed of light is 299,792.458 km/s.
在 .NET 5 及更早的 .NET 版本中,使用内插字符串隐式转换为 FormattableString 实例。 然后,可以使用实例 FormattableString.ToString(IFormatProvider) 方法或静态 FormattableString.Invariant 方法来生成特定于区域性的结果字符串。 下面的示例演示如何执行此操作:
double speedOfLight = 299792.458;
FormattableString message = $"The speed of light is {speedOfLight:N3} km/s.";
var specificCulture = System.Globalization.CultureInfo.GetCultureInfo("en-IN");
string messageInSpecificCulture = message.ToString(specificCulture);
Console.WriteLine(messageInSpecificCulture);
// Output:
// The speed of light is 2,99,792.458 km/s.
string messageInInvariantCulture = FormattableString.Invariant(message);
Console.WriteLine(messageInInvariantCulture);
// Output is:
// The speed of light is 299,792.458 km/s.
有关自定义格式设置的详细信息,请参阅在 .NET 中设置类型格式一文中的使用 ICustomFormatter 进行自定义格式设置部分。
其他资源
如果你不熟悉字符串内插,请参阅 C# 中的字符串内插交互式教程。 该教程演示了如何使用内插字符串生成带格式的字符串。
内插字符串编译
编译器检查内插字符串是否分配给满足 内插字符串处理程序模式的类型。 内插字符串处理程序是一种将内插字符串转换为结果字符串的类型。 当内插字符串的类型为 string 时,它由 System.Runtime.CompilerServices.DefaultInterpolatedStringHandler 处理。 有关自定义内插字符串处理程序的示例,请参阅编写自定义字符串内插处理程序教程。 使用内插字符串处理程序是一种高级方案,通常出于性能原因而需要使用。
注意
内插字符串处理程序的一个副作用是,自定义处理程序(包括 System.Runtime.CompilerServices.DefaultInterpolatedStringHandler)可能不会在所有条件下计算内插字符串中的所有内插表达式。 这意味着这些表达式的副作用可能不会发生。
如果内插字符串具有类型 string,它通常转换为 String.Format 方法调用。 如果分析的行为等同于串联,则编译器可将 String.Format 替换为 String.Concat。
如果内插字符串类型为 IFormattable 或 FormattableString,则编译器会生成对 FormattableStringFactory.Create 方法的调用。
C# 语言规范
有关详细信息,请参阅 C# 语言规范的内插字符串表达式部分以及以下新功能规范: