ホームに戻る
出典 :
関連 :
目次 :
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 プロパティと状態変数の値に応じて値を振り分ける例である。
宣言パターンについてはパターンマッチングを参照のこと。
ここでは、
- sender の型判別( Button 型かどうか)
- sender のメンバ( Tag )の識別
- 付帯条件(ここでは SubParam )の判定
を行っており、最初に評価されるアーム(1)では、
- sender 型が Button である
- ((Button)sender).Tag == "InkSys_1" である
- SubParam == true である
すべてが成立した場合にパターン(およびケースガード)に合致したものとみなされ、トークンに続く式
( "#1", "停止", SUPPLYFORINKSYS_1_OFF )
が switch 式の評価結果となる。
網羅的判定
アーム(1)の次に評価されるアーム(2)は、
- sender 型が Button である
- ((Button)sender).Tag == "InkSys_1" である
を条件としており、これは(1)の条件よりも緩いが、(1)の評価順のほうが早いため、(2)には(1)の条件に該当しなかったもののみが流れ込む。
(このため(2)には、when 句を記述する必要が無い。)
このように上段のアームでより限定的な判断を行い、該当しないもの( else / default )を下段で捕捉することで、すべての式を適切に評価できる。
一時変数
宣言パターンでは、評価対象がパターンにマッチした場合に評価対象を格納する一時変数をパターンに続けて指定することができる。
このとき、評価対象は合致した型にキャストされる。
上記の例ではアーム(1)~(4)に一時変数( b )を指定しており、アーム(1)評価時点で、
((Button)sender).Tag == "InkSys_1"
であれば、
b = (Button)sender
となる。
この一時変数は、when に続くケースガード内で参照することが可能である。
ただ、この例ではいずれのアームもケースガード内で b を参照していないため、省略しても問題は無い。