字串插補是 C# 中常用的特性,但由於其多變的用法,導致在一般專案開發中沒能充分活用。這篇文章將統整各種使用情境並加以說明。
因為字串插補編譯後的結果多樣,根據不同版本的 C# 可能有不同結果,這篇提到的編譯後結果只適用於該情境。
基本情境
1 | string Demo(string s1, string s2) |
Expression
因為 :
有特殊用途 (在後面的小節會提),所以三元運算子這種將 :
用於不同於字串插補用途的情境要把整個表達式用 ()
包起來。
1 | string Demo(string s1) |
換行與跳脫
可依照原始字串格式輸出,但 C# 11 前後版本語法差異很大。跳脫部分和一般字串一樣,除了大括號是1
{{}}
之外。
C# 111
2
3
4
5
6
7
8string Demo(string s1)
{
return $$"""
{
"s1": "{{s1}}"
}
""";
}
舊版:1
2
3
4
5
6string Demo(string s1)
{
return $@"{{
""s1"": ""{s1}""
}}";
}
1 | string Demo(string s1) |
多型別組合
通常隱含呼叫 ToString()
轉換
1 | string Demo(string s1, int i1) |
如果變數不是字串型別,”通常”會呼叫其 ToString()
方法來轉換成字串。
例外: 指派給 FormattableString
型別的變數時
參考 How to create a culture-specific result string with string interpolation
1 | using System.Globalization; |
編譯後的程式碼不像表面語法的那樣會馬上執行字串插補,而是改為呼叫 FormattableStringFactory.Create ("The value is {0:N}", d1)
,等到呼叫 formattable.ToString()
時才執行並參考 CultureInfo
,實際上是這樣運作的:1
2
3
4
5private string Demo(decimal d1, string culture)
{
FormattableString formattable = FormattableStringFactory.Create("The value is {0:N}", d1);
return formattable.ToString(CultureInfo.GetCultureInfo(culture));
}
例外: 指派給 FormattableString
型別的變數時 (.NET 6)
參考 How to create a culture-specific result string with string interpolation
1 | using System.Globalization; |
.NET 6 之後可以簡化成上面這樣,但是編譯後的結果和之前的版本不一樣,是改使用 DefaultInterpolatedStringHandler
如下:1
2
3
4
5
6
7
8
9private string Demo(decimal d1, string culture)
{
IFormatProvider cultureInfo = CultureInfo.GetCultureInfo(culture);
DefaultInterpolatedStringHandler val = default(DefaultInterpolatedStringHandler);
((DefaultInterpolatedStringHandler)(ref val))..ctor(13, 1, cultureInfo);
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral("The value is ");
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted<decimal>(d1, "N");
return string.Create(cultureInfo, ref val);
}
例外: IFormattable
參考 Compilation of interpolated strings
1 | public class CustomType : IFormattable |
實作 IFormattable
的型別在插補字串中,因為呼叫了 string.Format()
或是使用 DefaultInterpolatedStringHandler
(因 C# 與 .NET 版本不同而異) 最後是使用 IFormattable
提供的 ToString(...)
方法而不是 object.ToString()
。
空值防呆
1 | string Demo(string s1, int? i1) |
空值不會拋例外,不管編譯時視語言版本編譯成 string.Format
或 DefaultInterpolatedStringHandler
都支援空值。
對齊與格式化
細節參考 Structure of an interpolated string 中的各種連結
1 | $"Align:{"Ron",5}{100,4}".Dump(); // Align: Ron 100 |
結論
其實字串插補經過分類後並不會太複雜,只是因為各種情境下的分支細節太多,又有一些特例導致