在 C# 6 引入新的字串內插(string interpolation)的寫法,相比傳統的字串格式化寫法的可讀性還高。
使用 string.Format 需要先設定 formatString,最後在帶入多個 params 到 formatString 裡面來完成字串格式化。
int value1 = 16932;
int value2 = 15421;
string formatString = "{0:D} And {1:D} = {2:D}";
string result = String.Format(formatString, value1, value2, value1 & value2);
Console.WriteLine(result);
使用 interpolation 不需要設定 formatString,只需要在引號前面加上 $
編譯器就會知道這是個內插字串。
int value1 = 16932;
int value2 = 15421;
string interpolateString = $"{value1:D} And {value2:D} = {value1 & value2:D}" ;
Console.WriteLine(interpolateString);
從上面兩個例子可以發覺 string.Format 有一個最大的缺點就是參數留的空位與實際參數位置是分開的,假如參數多達十個以上會很難對應到空位裡, 或者會算錯實際參數的位置。
內插字串修改了這個最大的缺點,讓實際的空位跟參數可以寫在一起,可讀性提高非常多。
Boxing
需要注意在 C# 10 以前的寫法可以得知 interpolation 背後還是會呼叫 String.Format, 另外 Math.PI 會將回傳的 double 類型裝箱成 object 類型,這個操作會對效能產生影響。
Console.WriteLine($"The value of pi is {Math.PI}");
IL_000F 步驟為 boxing;
IL_0000 nop
IL_0001 ldstr "The value of pi is {0}"
IL_0006 ldc.r8 18 2D 44 54 FB 21 09 40 // 3.141592653589793
IL_000F box Double
IL_0014 call String.Format(String, Object)
IL_0019 call Console.WriteLine(String)
IL_001E nop
IL_001F ret
要避免 boxing 可以使用以下這個寫法直接將 double 轉成字串。
Console.WriteLine($"The value of pi is {Math.PI.ToString()}");
從 IL 碼可以得知沒有 box 呼叫,並且背後是使用 String.Concat 來將兩個字串合併起來。
IL_0000 nop
IL_0001 ldstr "The value of pi is "
IL_0006 ldc.r8 18 2D 44 54 FB 21 09 40 // 3.141592653589793
IL_000F stloc.0
IL_0010 ldloca.s 00
IL_0012 call Double.ToString()
IL_0017 call String.Concat(String, String)
IL_001C call Console.WriteLine(String)
IL_0021 nop
IL_0022 ret
根據 pull request 內容在 C# 10 和之後的版本添加了 InterpolatedStringHandler 修改了原本背後呼叫 String.Format 的行為,同時避免發生 boxing 的行為。
IL_0000 nop
IL_0001 ldloca.s 00
IL_0003 ldc.i4.s 13 // 19
IL_0005 ldc.i4.1
IL_0006 call DefaultInterpolatedStringHandler..ctor
IL_000B ldloca.s 00
IL_000D ldstr "The value of pi is "
IL_0012 call DefaultInterpolatedStringHandler.AppendLiteral(String)
IL_0017 nop
IL_0018 ldloca.s 00
IL_001A ldc.r8 18 2D 44 54 FB 21 09 40 // 3.141592653589793
IL_0023 call DefaultInterpolatedStringHandler.AppendFormatted<Double>(Double)
IL_0028 nop
IL_0029 ldloca.s 00
IL_002B call DefaultInterpolatedStringHandler.ToStringAndClear()
IL_0030 call Console.WriteLine(String)
IL_0035 nop
IL_0036 ret
Summary
使用 String Interpolation
或 StringBuilder
取代 string.Format
可以獲得更好的效能。
如果很在意效能建議是直接手動呼叫 ToString() 將值型別轉成參考型別,但是新版的寫法已經避免了 boxing 所以兩種寫法相差不多。
另外要注意不要直接用字串內插來寫 SQL 語法,因為會導致 SQL Injection 的問題。