C# 中的字符串内插

本教程演示如何使用字符串插值设置表达式结果的格式并将其包含仅结果字符串中。 以下示例假设阅读者熟悉基础 C# 概念和 .NET 类型格式设置。 如果不熟悉字符串插值或 .NET 类型格式设置,请先参阅交互式字符串内插教程。 若要详细了解如何在 .NET 中设置类型的格式,请参阅设置 .NET 中类型的格式

介绍

若要将字符串标识为内插字符串,可在该字符串前面加上 $ 符号。 可嵌入任何会在内插字符串中返回值的有效 C# 表达式。 在以下示例中,对某个表达式执行计算后,其结果立即转换为一个字符串并包含到结果字符串中:

double a = 3;
double b = 4;
Console.WriteLine($"Area of the right triangle with legs of {a} and {b} is {0.5 * a * b}");
Console.WriteLine($"Length of the hypotenuse of the right triangle with legs of {a} and {b} is {CalculateHypotenuse(a, b)}");
double CalculateHypotenuse(double leg1, double leg2) => Math.Sqrt(leg1 * leg1 + leg2 * leg2);
// Output:
// Area of the right triangle with legs of 3 and 4 is 6
// Length of the hypotenuse of the right triangle with legs of 3 and 4 is 5

如示例所示,通过将表达式用大括号括起来,可将表达式包含到内插字符串中:

{<interpolationExpression>}

内插字符串支持字符串复合格式设置功能的所有功能。 这使得它们成为 String.Format 方法的更具可读性的替代选项。

如何为内插表达式指定格式字符串

若要指定受表达式结果类型支持的格式字符串,请在内插表达式后面添加冒号(“:”)和格式字符串:

{<interpolationExpression>:<formatString>}

以下示例演示如何为生成日期和时间或数值结果的表达式指定标准和自定义格式字符串:

var date = new DateTime(1731, 11, 25);
Console.WriteLine($"On {date:dddd, MMMM dd, yyyy} L. Euler introduced the letter e to denote {Math.E:F5}.");
// Output:
// On Sunday, November 25, 1731 L. Euler introduced the letter e to denote 2.71828.

有关详细信息,请参阅复合格式设置一文的格式字符串组件部分。

如何控制设置了格式的内插表达式的字段宽度和对齐方式

若要指定设置了格式的表达式结果的最小字段宽度和对齐方式,请在内插表达式后添加逗号(“,”)和常数表达式:

{<interpolationExpression>,<alignment>}

如果对齐方式值为正,则设置了格式的表达式结果为右对齐,如果为负,则为左对齐

如果需要同时指定对齐方式和格式字符串,则先从对齐方式组件开始:

{<interpolationExpression>,<alignment>:<formatString>}

以下示例演示如何指定对齐方式并使用管道字符 ("|") 分隔文本字段:

const int NameAlignment = -9;
const int ValueAlignment = 7;
double a = 3;
double b = 4;
Console.WriteLine($"Three classical Pythagorean means of {a} and {b}:");
Console.WriteLine($"|{"Arithmetic",NameAlignment}|{0.5 * (a + b),ValueAlignment:F3}|");
Console.WriteLine($"|{"Geometric",NameAlignment}|{Math.Sqrt(a * b),ValueAlignment:F3}|");
Console.WriteLine($"|{"Harmonic",NameAlignment}|{2 / (1 / a + 1 / b),ValueAlignment:F3}|");
// Output:
// Three classical Pythagorean means of 3 and 4:
// |Arithmetic|  3.500|
// |Geometric|  3.464|
// |Harmonic |  3.429|

如示例输出所示,如果已设置格式的表达式结果长度超出指定字段宽度,则忽略对齐方式值

有关详细信息,请参阅复合格式设置一文的对齐组件部分。

如何在内插字符串中使用转义序列

内插字符串支持所有可在普通字符串文本中使用的转义序列。 有关详细信息,请参阅字符串转义序列

若要逐字解释转义序列,可使用逐字字符串文本。 内插逐字字符串以 $@ 字符开头。 可以按任意顺序使用 $@$@"..."@$"..." 均为有效的内插逐字字符串。

若要在结果字符串中包含大括号 "{" 或 "}",请使用两个大括号 "{{" 或 "}}"。 有关详细信息,请参阅复合格式设置一文的转义括号部分。

以下示例演示如何在结果字符串中包含大括号并构造逐字内插字符串:

var xs = new int[] { 1, 2, 7, 9 };
var ys = new int[] { 7, 9, 12 };
Console.WriteLine($"Find the intersection of the {{{string.Join(", ",xs)}}} and {{{string.Join(", ",ys)}}} sets.");
// Output:
// Find the intersection of the {1, 2, 7, 9} and {7, 9, 12} sets.

var userName = "Jane";
var stringWithEscapes = $"C:\\Users\\{userName}\\Documents";
var verbatimInterpolated = $@"C:\Users\{userName}\Documents";
Console.WriteLine(stringWithEscapes);
Console.WriteLine(verbatimInterpolated);
// Output:
// C:\Users\Jane\Documents
// C:\Users\Jane\Documents

从 C# 11 开始,可以使用内插的原始字符串文本

如何在内插表达式中使用三元条件运算符 ?:

因为冒号 (:) 在具有内插表达式的项中具有特殊含义,为了在表达式中使用条件运算符,请将表达式放在括号内,如下例所示:

var rand = new Random();
for (int i = 0; i < 7; i++)
{
    Console.WriteLine($"Coin flip: {(rand.NextDouble() < 0.5 ? "heads" : "tails")}");
}

如何使用字符串插值创建区域性特定的结果字符串

默认情况下,内插字符串将 CultureInfo.CurrentCulture 属性定义的当前区域性用于所有格式设置操作。

从 .NET 6 开始,可以使用 String.Create(IFormatProvider, DefaultInterpolatedStringHandler) 方法将内插字符串解析为特定于区域性的结果字符串,如以下示例所示:

var cultures = new System.Globalization.CultureInfo[]
{
    System.Globalization.CultureInfo.GetCultureInfo("en-US"),
    System.Globalization.CultureInfo.GetCultureInfo("en-GB"),
    System.Globalization.CultureInfo.GetCultureInfo("nl-NL"),
    System.Globalization.CultureInfo.InvariantCulture
};
var date = DateTime.Now;
var number = 31_415_926.536;
foreach (var culture in cultures)
{
    var cultureSpecificMessage = string.Create(culture, $"{date,23}{number,20:N3}");
    Console.WriteLine($"{culture.Name,-10}{cultureSpecificMessage}");
}
// Output is similar to:
// en-US       8/27/2023 12:35:31 PM      31,415,926.536
// en-GB         27/08/2023 12:35:31      31,415,926.536
// nl-NL         27-08-2023 12:35:31      31.415.926,536
//               08/27/2023 12:35:31      31,415,926.536

在更早的 .NET 版本中,请使用从内插的字符串到 System.FormattableString 实例的隐式转换,并调用它的 ToString(IFormatProvider) 方法来创建特定于区域性的结果字符串。 下面的示例演示如何执行此操作:

var cultures = new System.Globalization.CultureInfo[]
{
    System.Globalization.CultureInfo.GetCultureInfo("en-US"),
    System.Globalization.CultureInfo.GetCultureInfo("en-GB"),
    System.Globalization.CultureInfo.GetCultureInfo("nl-NL"),
    System.Globalization.CultureInfo.InvariantCulture
};
var date = DateTime.Now;
var number = 31_415_926.536;
FormattableString message = $"{date,23}{number,20:N3}";
foreach (var culture in cultures)
{
    var cultureSpecificMessage = message.ToString(culture);
    Console.WriteLine($"{culture.Name,-10}{cultureSpecificMessage}");
}
// Output is similar to:
// en-US       8/27/2023 12:35:31 PM      31,415,926.536
// en-GB         27/08/2023 12:35:31      31,415,926.536
// nl-NL         27-08-2023 12:35:31      31.415.926,536
//               08/27/2023 12:35:31      31,415,926.536

如示例所示,可使用某个 FormattableString 实例为各种区域性生成多个结果字符串。

如何使用固定区域性创建结果字符串

从 .NET 6 开始,请使用 String.Create(IFormatProvider, DefaultInterpolatedStringHandler) 方法将内插字符串解析为 InvariantCulture 的结果字符串,如以下示例所示:

string message = string.Create(CultureInfo.InvariantCulture, $"Date and time in invariant culture: {DateTime.Now}");
Console.WriteLine(message);
// Output is similar to:
// Date and time in invariant culture: 05/17/2018 15:46:24

在更早版本的 .NET 以及 FormattableString.ToString(IFormatProvider) 方法中,可以使用静态 FormattableString.Invariant 方法,如以下示例所示:

string message = FormattableString.Invariant($"Date and time in invariant culture: {DateTime.Now}");
Console.WriteLine(message);
// Output is similar to:
// Date and time in invariant culture: 05/17/2018 15:46:24

结论

本教程介绍字符串插值用法的常见方案。 有关字符串内插的详细信息,请参阅字符串内插。 若要详细了解如何在 .NET 中设置类型的格式,请参阅这两篇文章:设置 .NET 中类型的格式复合格式设置

另请参阅