はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】string-interpolation(文字列補間)がC#10.0からboxingが発生しないようになる修正が入ったらしい(DefaultInterpolatedStringHandler)

はじめに

今回はstring-interpolationの実態がどうなっているかについて取り上げたいと思います。

var x = 10;
var y = 20;
var str = $"{x}, {y}";

C#9時点

SharpLabにて先ほどのコードをC#9ILにコンパイルをし、C#に逆コンパイルをするという手順をとると以下のようなコードが生成されます。

C#9

なんと以下のコードは実質的に全く同じであるということが分かります。

var str = $"{x}, {y}";
var str = string.Format("{0}, {1}", x, y);

ちなみにですが、string.Formatの引数はObjectなので、xyがスタックに配置されている場合はboxingが発生してしまいます。
String.Format メソッド (System) | Microsoft Learn

これはResultsILに変換すると調べることができます。

IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldstr "{0}, {1}"
IL_000b: ldloc.0
IL_000c: box [System.Private.CoreLib]System.Int32
IL_0011: ldloc.1
IL_0012: box [System.Private.CoreLib]System.Int32
IL_0017: call string [System.Private.CoreLib]System.String::Format(string, object, object)
IL_001c: pop
IL_001d: ret

IL_000cIL_0012がそれですね。

defalt(C#10.0以上)

コンパイラーを変えてみると、実は結果が変わります。

// 変換前
var x = 10;
var y = 20;
var str = $"{x}, {y}";
// 変換後
int value = 10;
int value2 = 20;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(2, 2);
defaultInterpolatedStringHandler.AppendFormatted(value);
defaultInterpolatedStringHandler.AppendLiteral(", ");
defaultInterpolatedStringHandler.AppendFormatted(value2);
defaultInterpolatedStringHandler.ToStringAndClear();

よくわかりませんがDefaultInterpolatedStringHandlerを利用したコードに変換されました。

補間された文字列 String をインスタンスに処理するために言語コンパイラによって使用されるハンドラーを提供します。

learn.microsoft.com

前回と同じようにILを見てboxingが発生していないか調べてみましょう。

IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldloca.s 2
IL_0008: ldc.i4.2
IL_0009: ldc.i4.2
IL_000a: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::.ctor(int32, int32)
IL_000f: ldloca.s 2
IL_0011: ldloc.0
IL_0012: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<int32>(!!0)
IL_0017: ldloca.s 2
IL_0019: ldstr ", "
IL_001e: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string)
IL_0023: ldloca.s 2
IL_0025: ldloc.1
IL_0026: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<int32>(!!0)
IL_002b: ldloca.s 2
IL_002d: call instance string [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear()
IL_0032: pop
IL_0033: ret

どこにもboxの文字がありません。つまりstring-interpolationboxingが発生しないような修正が裏で入ったということになります。

Unityとの関係性について

Unity 2022.1の段階でもC#9.0までしか対応していません。

またDefaultInterpolatedStringHandler .NET 6.0から、つまりC#10.0から導入されたようです。
DefaultInterpolatedStringHandler.AppendFormatted メソッド (System.Runtime.CompilerServices) | Microsoft Learn
C# 言語のバージョン管理 - C# ガイド | Microsoft Learn