本セッションの登壇者
セッション動画(YouTubeチャンネル登録もお願いします。)
それでは始めたいと思います、よろしくお願いします。
「5分で学ぶInterpolated String Handler - C#上級テクニック編 -」ということで、今回はふだんC#コードを書いている範囲ではまったく見ることのないようなニッチな部分について説明させていただこうと思います。
私は Microsoft MVPアワードをC#のカテゴリで10年連続でいただいている「C#ヲタ」なんですけれども、今日は私のことは正直どうでもよいです。
本日の主役はこちらです。
string interpolationという機能ですね。JavaScriptだとtemplate literalのようにいうと思います。
これは、実はC#に新しく追加された機能ではなく、もう8年ほど前から使われている機能です。なのでC #erは基本的には毎日これを書いているわけで、なんで今回これを取り上げるのか、ということなんですが...。
実は、半年ほど前にリリースされた C# 10 / .NET 6で大幅な高速化が図られました。
これは、コンパイラがチューニングしてくれた結果です。
一切ユーザーコードを書き換えることなく、このまま.NET 6で再コンパイルするだけでパフォーマンス向上の恩恵を受けられる、というものですね。
あわせて、独自のカスタマイズが可能になっています。内部的には、コンパイラはパターンベースな解釈で、特定の型に依存しないような実装になっています。なので、その部分に独自のコードを差し込むことが可能になります。
本日のスライドはここまでで、ここからは実際のコードを見ていこうと思います。
string型を独自の型に
実際のコードをお見せすると、左側がC# 10、右側がC# 9で書いたコードです。
まったく同じコードなんですけれども、コンパイラがそれぞれどのように展開するかを見ていきたいと思います。
コンパイルするとこのようになるんですね。上が C# 10、下がC# 9のものです。
C# 10だと、DefaultInterpolatedStringHandlerというオブジェクトのAppendLiteral、AppendFormattedというメソッドの呼び出しチェーンに書き変わっています。
一方、C# 9までだと、String.Formatに展開されています。このString.Formatは、値型から参照型へのボックス化だったり、Object型の配列をメモリアロケーションするなど、あまり効率のよい書き方ではありません。
それを、コンパイラが上記のようなまったく別の記法に展開することで、その辺のチューニングがなされるようになりました。
そして、このDefaultInterpolatedStringHandlerは、string型に対する実装が既定の実装なのですが、この部分を独自の型に差し替えることができる、というのが今回のポイントです。
実際にどのように書くか、やってみたいと思います。
SampleHandlerという型を用意してみました。コンストラクタと AppendLiteral、AppendFormatted というメソッドをそれぞれ用意しています。この2つは、先ほどコンパイラが展開したメソッドですね。
SampleHandlerクラスの属性がポイントで、[Interpolated String Handler] という属性がついている型であればよい、とコンパイラが認識してくれます。
既定はstring型ですが、これを独自の型に差し替えてみます。型を差し替えても、コンパイルが通るんですね。コンパイルが通った結果どういう風になるかというと...。
このようにSampleHandlerという型にコンパイラが展開を変えてくれました。
AppendLiteralやAppendFormattedのメソッドがSampleHandlerのものとして呼ばれるので、メソッドに独自の実装を書ける、ということになります。
戻り値をbool型に
ほかにも細かい仕様がいくつかありますが、戻り値をbool型にする、というものがあります。
このように戻り値をbool型にできます。
何が変わるのか、コンパイラが展開したものを見てみます。
このように、展開結果がまったく別のものに変わりました。
if文の中でSampleHandlerのメソッドの かつ(&&) 条件の展開に変わっています。
なので、このAppendLiteralやAppendFormattedの途中で処理を打ち切りたいときに、メソッドがreturn falseするように実装すれば、途中で処理をやめられるようなカスタマイズが可能になります。
最後に、メソッドの実装を変えたものを動かしてみたいと思います。
AppendLiteralやAppendFormattedの中でConsole.WriteLineを実行する実装に変えてみました。
この状態でコンパイルして実行してみたいと思います。
すると、このような実行結果になります。
独自の型の実装で呼び出すことができています。
このように、コンパイラのハックという形で、コンパイラが提供している拡張ポイントに対して自分のコードを差し込むことで、好きなことができるようになります。
実はまだまだ細かい仕様がいっぱいあります。
その辺について知りたいなと思う方がいらっしゃったら、公式ドキュメントを参照して実際に試していただけたらと思います。
本日はご清聴ありがとうございました。