ホームに戻る
出典 :
switch 式 - C# リファレンス - Microsoft Learn 選択ステートメント - C# リファレンス | Microsoft Learn パターン - C# リファレンス | Microsoft Learn
関連 :
型判別、型キャスト、as・is パターンマッチング
目次 :

switch 式とは

C#8.0で追加された、入力値に応じて出力値を選択できる構文。switch - case 節を用いるよりも記述を簡略化でき、かつ選択を網羅的にできる。

switch - case 節( switch ステートメント)との比較

switch - case 節による実装

public static Orientation ToOrientation( Direction direction ) { switch( direction ) { case Direction.Up: return Orientation.North; case Direction.Right: return Orientation.East; case Direction.Down: return Orientation.South; case Direction.Left: return Orientation.West; default: throw new ArgumentOutOfRangeException(nameof(direction), $"Not expected direction value: {direction}"); } }
ここでは入力値( direction )に応じて戻り値( Orientation )の選択を行っている。
この実装は、関数が値を戻すことが構造的に保証されていない。このため、一部の開発環境では警告が出ることがある。
( case / default を変更・追加する場合、return 文(または例外送出)の記述が必須となる。
論理構成上値を戻さない(かつ例外を送出しない)パスは存在しないが、将来変更が加えられた場合はその限りではない。所謂「良心に依存したコード」となっている。)

switch 式による実装

public static Orientation ToOrientation( Direction direction ) => direction switch { Direction.Up => Orientation.North, Direction.Right => Orientation.East, Direction.Down => Orientation.South, Direction.Left => Orientation.West, _ => throw new ArgumentOutOfRangeException(nameof(direction), $"Not expected direction value: {direction}"), };
先頭行の direction switch から最終行の } (ブレース)までが switch 式である。
ここでは switch 式の値を戻り値としており、direction の値によらず switch 式の評価結果はひとつの型( Orientation )に固定される。
このため関数が値を戻す(または例外を送出する)ことが構造的に保証された堅牢なコードとなっている。
また switch - case 節を用いる場合と比べ、記述は簡素となる。

switch 式の構成


通常、switch 式は複数の「アーム」( switch ステートメントにおける case 節に相当)で構成され、各アームは条件判定を行う「パターン」と、パターンに合致した際に swtich 式の評価値となる「式」からなる。
パターンにはリテラルをはじめとした定数式だけでなく、パターンマッチングに基づくルールを指定可能。これは switch ステートメントも同様である。 パターンのみでは十分な判定ができない場合に、付帯条件として「ケースガード」を指定できる。
ケースガードは when キーワードと、それに続くブール式として指定する。
なお、アームは必ず記述した順に(上から)判定が行われる。このため、後述の「破棄パターン」は必ず末尾に記述する

破棄パターン

_ (アンダースコア)で表現される「破棄パターン」は null を含む任意の式に一致する。( switch ステートメントにおける defalt に相当。)
入力値がいずれのアームにも適合しなければ、switch 式の評価値が決定できず実行時エラーとなるが、
破棄パターンを用いるとすべての入力式が処理され、式の評価値は必ず確定する。

switch 式の具体例

private void Button_Click( object sender, RoutedEventArgs e ) { // ボタンのタグと SubParam に基づいて処理を決定 (string tgt, string ope, E_CommandForPlc cmd) = sender switch { Button { Tag: "InkSys_1" } b when SubParam => ( "#1", "停止", SUPPLYFORINKSYS_1_OFF ), //< 1) インクシステム#1 オン ⇒ オフ Button { Tag: "InkSys_1" } b => ( "#1", "開始", SUPPLYFORINKSYS_1_ON ), //< 2) インクシステム#1 オフ ⇒ オン Button { Tag: "InkSys_2" } b when SubParam => ( "#2", "停止", SUPPLYFORINKSYS_2_OFF ), //< 3) インクシステム#2 オン ⇒ オフ Button { Tag: "InkSys_2" } b => ( "#2", "開始", SUPPLYFORINKSYS_2_ON ), //< 4) インクシステム#2 オフ ⇒ オン _ => throw new Exception( "規定されていない動作です。" ), //< 5) 上記以外 (破棄パターン) }; // 問い合わせ表示、「はい」押下時以外は抜ける if( MessageBoxFunc.ShowYesNoMsgBox( $"インクシステム{tgt}へのインク供給を{ope}します。\nよろしいですか?" ) != MessageBoxFunc.E_Result.Yes ) { return; } // コマンド発行 MainControl.DoPostRequestToPlc( cmd ); }
上記は宣言パターンを用いた、Button の Click ハンドラ内で Tag プロパティと状態変数の値に応じて値を振り分ける例である。
宣言パターンについてはパターンマッチングを参照のこと。
ここでは、 を行っており、最初に評価されるアーム(1)では、 すべてが成立した場合にパターン(およびケースガード)に合致したものとみなされ、トークンに続く式
( "#1", "停止", SUPPLYFORINKSYS_1_OFF )
が switch 式の評価結果となる。

網羅的判定

アーム(1)の次に評価されるアーム(2)は、 を条件としており、これは(1)の条件よりも緩いが、(1)の評価順のほうが早いため、(2)には(1)の条件に該当しなかったもののみが流れ込む。
(このため(2)には、when 句を記述する必要が無い。)

このように上段のアームでより限定的な判断を行い、該当しないもの( else / default )を下段で捕捉することで、すべての式を適切に評価できる。

一時変数

宣言パターンでは、評価対象がパターンにマッチした場合に評価対象を格納する一時変数をパターンに続けて指定することができる。
このとき、評価対象は合致した型にキャストされる。
上記の例ではアーム(1)~(4)に一時変数( b )を指定しており、アーム(1)評価時点で、
((Button)sender).Tag == "InkSys_1"
であれば、
b = (Button)sender
となる。
この一時変数は、when に続くケースガード内で参照することが可能である。
ただ、この例ではいずれのアームもケースガード内で b を参照していないため、省略しても問題は無い。