C# 中的字符串内插

本教程介绍如何使用 字符串内插 设置结果的格式,并在结果字符串中包含表达式结果。 这些示例假定你熟悉基本的 C# 概念和 .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 该方法的更可读的替代方法。 每个内插字符串必须具有:

  • $字符开头的字符串文字,其左引号字符之前。 符号和引号字符之间 $ 不能有任何空格。
  • 一个或多个 内插表达式。 指示带有左大括号和右大括号({})的内插表达式。 可以将任何返回值(包括 null)的 C# 表达式放在大括号内。

C# 按照以下规则计算在字符{}之间的表达式:

  • 如果内插表达式的计算结果为null,则使用空字符串(“”或String.Empty)。
  • 如果插值表达式的求值结果不是null,通常会调用结果类型的ToString方法。

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

若要指定一个格式字符串,该字符串由表达式结果的类型支持,请在内插表达式后加上冒号(“:”)和格式字符串。

{<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>,<width>}

下面的代码示例使用最小字段宽度来创建表格输出:

var titles = new Dictionary<string, string>()
{
    ["Doyle, Arthur Conan"] = "Hound of the Baskervilles, The",
    ["London, Jack"] = "Call of the Wild, The",
    ["Shakespeare, William"] = "Tempest, The"
};

Console.WriteLine("Author and Title List");
Console.WriteLine();
Console.WriteLine($"|{"Author",-25}|{"Title",30}|");
foreach (var title in titles)
{
    Console.WriteLine($"|{title.Key,-25}|{title.Value,30}|");
}
// Output:
// Author and Title List
// 
// |Author                   |Title                          |
// |Doyle, Arthur Conan      |Hound of the Baskervilles, The |
// |London, Jack             |         Call of the Wild, The |
// |Shakespeare, William     |                  Tempest, The |

如果 宽度 值为正值,则格式化的表达式结果右对齐;如果为负,则为左对齐。 -删除宽度说明符之前的符号,然后再次运行示例以查看结果。

如果需要同时指定宽度和格式字符串,请从宽度组件开始:

{<interpolationExpression>,<width>:<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|

如示例输出所示,如果格式化表达式结果的长度超过指定的字段宽度,则忽略 宽度 值。

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

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

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

若要逐字解释转义序列,可使用逐字字符串文本。 插值逐字字符串以$@字符开始。 可以以任意顺序使用$@$@"..."@$"..."都是有效的逐字插值字符串。

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

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

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 和复合格式设置文章中的 格式设置 类型。

另请参阅