C# $ 字符串插值
#$ 字符
字符串插值(String Interpolation)有许多的实现方式,其中使用 $
字符在现代 C# 中时比较推荐的方式,它提供类似于 String.Format
的效果。
实际根据 $
使用场景的不同,编译器会选择用不同的方式实现字符串插值,String.Format
只是其中一种,具体见 实现细节
如下分别为使用 $
和 String.Format
的示例:
1 | int value = 3; |
输出结果为:
1 | Value is 3 |
#使用方式
#创建内插字符串
字符串插值(String Interpolation)
是用来将表达式插入到字符串中的方式,简单的示例如下所示:
1 | string name = "wxj"; |
输出结果为:
1 | Hello,wxj. |
其中 $"Hello,{name}.")
被称为 内插字符串表达式(interpolated string expression)
(下简称 内插表达式
),最后输出的 Hello,wxj.
被称为 结果字符串(result string)
由上例可以看出字符串插值的两个必要因素:
- 在字符串前需要有
$
字符标记,且该字符与后续的"
间不能有空格。 - 在内插表达式内部可以有一个或多个
{}
,其中包含着任何返回结果的 C# 表达式,表达式的返回值也可以为null
。
#包含不同的数据类型
对于内插表达式中的各 C# 表达式可以是任何类型的,如下所示:
1 | var item = (Name: "eggplant", Price: 1.99m, perPackage: 3); |
输出结果为:
1 | On 12/10/2021 8:26:07 AM, the price of eggplant was 1.99 per 3 items. |
可以看到该内插字符串表达式中包含有各种类型的表达式(string
,Decimal
,int
,DeltaTime
),在最终的结果中都被正确的解析。
内插字符串表达式,各表达式都会被转换为 string
,且规则如下:
- 如果表达式结果为
null
,将其转换为空字符串。 - 如果表达式结果不为
null
,对其调用ToString
函数。
#控制内插表达式的格式
在内插表达式中,还可以控制各表达式转换到 string
时的格式,如下所示:
1 | DateTime date = DateTime.Now; |
输出结果为:
1 | On 12/10/2021 8:39:56 AM, value is 1.123457 |
在内插表达式中的各表达式中可以通过 :
后加控制的字符格式化输出。如上例中的 d
和 f3
即为控制字符。
:
后的控制字符,相当于在调用 ToString
时作为形参控制表达式的输出。
上述表达式等同于:
1 | Debug.Log("On " + date.ToString("d") + ", value is " + value.ToString("f3")); |
#控制内插表达式的对齐方式
在内插表达式中的个表达式中可以通过 ,
后加数字来控制字符宽度和对其方式,如下所示:
1 | var inventory = new Dictionary<string, int>() |
输出结果为:
1 | |Item | Quantity| |
其中逗号后的数字,如果为负数,则输出为左对齐,如果为正数则右对齐。数字本身表示最少的字符数。因此如果显示系统中每个字符的宽度是不相等的话,如 i
和 a
的宽度在某些显示系统下会有较大差异,则即使控制字符宽度也无法实现对其的效果。
如下为相同输出结果在 Unity 的 Console 面板中的展示:
表达式格式和对齐方式也可以一起设定,但需要首先设定对其方式,再设定格式。如下首先控制了左对齐,且字符数为 10 个,又设定输出格式为当前小时数:
1 | Debug.Log($"[{DateTime.Now,-10:HH}]"); |
结果为:
1 | [10 ] |
#内插表达式中使用转义序列
如果要在内插表达式中可以使用转义序列,当需要多次使用转义序列时也可使用 原义标识符@ 替代。
如下所示,分别使用使用了转义序列和原文标识符:
1 | string userName = "wxj"; |
输出结果为:
1 | C:\Users\wxj\Documents |
C# 8.0 后,$
与 @
的先后顺序不会造成任何影响。在早期版本中,必须先写 $
再写 @
。
在内插表达式中,如果需要输入 {
,则按如下方式处理:
1 | int[] values = new int[] { 1, 2 }; |
输出结果为:
1 | Value is {1, 2} |
也可以通过 $@
的结合控制换行,如下:
1 | var publishDate = new DateTime(2017, 12, 14); |
此时输出为:
1 | This post published on 2017-12-14 is about |
#内插表达式中使用 ?: 运算符
因为 :
在内插表达式中用来指定格式,因此当使用 ?:
运算符时,必须定义在括号内。如下所示:
1 | System.Random random = new System.Random(); |
输出结果为:
1 | Even |
#实现细节
根据 $
的实现方式的不同,编译器会选择用不同的方式实现字符串插值。
#string.Concat
当插值的对象为类型为 string 时,编译器会选择使用 string.Concat
。
如下代码:
1 | string name = "meziantou"; |
编译器会将其转换为:
1 | string name = "meziantou"; |
#string.Format
如果插值的对象为类型为非 string 时,编译器会选择使用 string.Format
。
如下代码:
1 | DateTime now = DateTime.Now; |
编译器会将其转换为:
1 | DateTime now = DateTime.Now; |
#FormattableString
如果插值的对象为类型为 Formattable String 时,编译器会选择使用 FormattableStringFactory.Create
创建一个新的 FormattableString
。
如下代码:
1 | object value1 = "Foo"; |
编译器会将其转换为:
1 | object value1 = "Foo"; |
当真正需要使用该 string 时,会调用 FormattableString.ToString
方法,将其转换为 string。
#constants(C# 10)
在 C# 10 中,支持将内插表达式的结果作为常量。如下所示:
1 | const string Username = "meziantou"; |
#Interpolated string handlers(C# 10)
C# 10 中针对高性能场景,引入了 InterpolatedStringHandlerArgument
,具体见:
String Interpolation in C# 10 and .NET 6 - .NET Blog (microsoft.com)
#Reference
$ - string interpolation - C# reference | Microsoft Docs
String interpolation - C# tutorial | Microsoft Docs