はじめに
この記事はDeep C# vol.3 – await usingに続く記事となります。
第4回 Deep C# は switch-expression(switch式) を分析します。
switch式とは
C#7以降、パターンマッチングの一環として switch を式として書くことが出来る拡張も行われてきました。これは switch式 / switch expression と呼ばれています。
一般には「switch式」と呼ばれることが多いようです。
switch式はこのように記述します。
1 2 3 4 5 6 7 8 9 10 |
var value = obj switch { "test" => "test message", int number when number % 2 == 0 => number + " : 偶数", int number when number % 2 == 1 => number + " : 奇数", DateTime time => time.ToLongDateString().ToLower(), string str => str.ToLower(), _ => string.Empty, // どれにも当てはまらなかった場合。 default: と同じ意味 }; Console.WriteLine(value); |
従来の switch文 と比較すると case: - break;
の必要がないサッパリとしたコードを記述できるメリットがあります。
そして、パターンマッチングの進歩とともに caseに相当する部分には 型、値、条件式などを記述することが出来るようになりました。
switch式を解体する
パターンマッチングの複雑性が原因で、switch式 は逆コンパイル時に switch式/switch文には変換できないようになってしまいました。見る影もありません。
逆コンパイルすると大量の if - else
や goto
が出力されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
object obj2 = obj; if (1 == 0) { } // このコードが出力される理由は不明です string str = obj2 as string; string text; if (str != null) { text = ((!(str == "test")) ? str.ToLower() : "test message"); } else if (obj2 is int) { int number2 = (int)obj2; if (number2 % 2 == 0) { text = number2 + " : 偶数"; } else { int number = number2; if (number % 2 != 1) { goto IL_02f8; // _ => string.Empty の処理にジャンプする } text = number + " : 奇数"; } } else { if (!(obj2 is DateTime)) { goto IL_02f8; // _ => string.Empty の処理にジャンプする } text = ((DateTime)obj2).ToLongDateString().ToLower(); } goto IL_0301; IL_0301: if (1 == 0) { } // このコードが出力される理由は不明です string value = text; // switch式の後の部分はここにあります Console.WriteLine(value); return; IL_02f8: // default の処理はスコープの最後に記述されます text = string.Empty; goto IL_0301; |
まず、最初に型判定を行うif文が並びます。その中に値の比較や when に書かれた条件式の判定が入ります。
最後にそれらを式の内容で変換して、value 変数に格納し、後続処理に続きます。
default 相当の処理はスコープの末尾に展開されており goto で行き来します。
コンパイラーの出力は、最も軽量なコードを目指していますので、この goto を用いたコンパイル結果は正しいものだと言えます。
途中、if (1 == 0) { }
という謎のコードを2つ見つけましたが、これは実行時の最適化で消えるので無害です。
まとめ
パターンマッチング自体は型、値、式などを判定する if文に変換するものでした。
switch式は、switch文を短く書くことが出来るケースならば積極的に活用したいものです。
同じ内容の switch文を考えるとかなりの記述量を圧縮できることがわかるはずです。
またこれまでの Deep C# で扱ってきたものと同様、.NET の仕様自体には変更を加えず、コードの出力の工夫で新しい構文を実現していることが分かります。
他のさまざまな構文がどのような C# に展開されるかを理解することは、言語のより深い理解を意味します。
今後も Deep C# はこの方向を突き詰めていく予定です。
次回は yield return です。ご期待ください。