switchステートメント
基本的なswitch文
switch文(switchステートメント)を使うと、指定した式(変数など)の値によって処理を分岐させることができる。
caseでパターンを指定するため、switch-case文とも呼ばれる。
最も基本的なswitch文は、caseの後に定数(定数パターン)を用いるものである。定数パターンには文字列や整数が使える。
以下の例では変数countryの値によって処理を振り分け、それぞれにふさわしい国名コードを出力する。
countryがいずれのcaseにも該当しなければdefaultが実行される。もしdefaultがなければ何もしない。
case ○○: やdefault:の末尾はセミコロンではなくコロンなので注意。
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 |
var country = "アメリカ"; Console.Write("国名コードは"); switch (country) { case "日本": Console.Write("JP"); break; case "アメリカ": Console.Write("US"); break; case "フランス": Console.Write("FR"); break; case "スペイン": Console.Write("ES"); break; default : Console.Write("不明です"); break; } |
フォールスルーの禁止
C言語など他の言語では、1つのcase区画の終点に達すると次のcase区画に自動的に移るフォールスルーをサポートしているものも多い。
しかし、実際にはbreakの書き忘れであることが多いため、C♯ではフォールスルーはサポートされていない。また、C#コンパイラは各case区画の終点に到達してはいけないというルールがある。
上記のことから、C#ではcase区画の最後にbreak;を書かないとコンパイルエラーになる。
ただし実際はbreak;に限らず、switchの外に実行を移すものであれば何でも使える。(後述)
もしフォールスルーを実装したい場合は後述するgoto caseを使う。
caseに複数の処理を書く/同じ処理をするcaseをまとめて書く
case区画には複数のステートメント(文)を記述することもできる。
また、同じ処理をするラベルがある場合はcaseをまとめて書くことができる。
以下の例は変数statusの値によって表示内容を変えるswitchステートメント。statusは”締め切り直前”なので、「首の皮一枚」「もっとがんばれ」と表示される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
string status = "締め切り直前"; switch (status) { case "締め切り前": Console.WriteLine("昇給"); Console.WriteLine("おめでとう"); break; case "締め切り直前"://ラベルを複数指定・・・どちらかが成り立つとき実行 case "締め切り当日": Console.WriteLine("首の皮一枚"); Console.WriteLine("もっとがんばれ"); break; case "締め切り超過": Console.WriteLine("解雇"); Console.WriteLine("さようなら"); break; default: Console.WriteLine("早く仕事しろ"); break; } |
1 2 |
首の皮一枚 もっとがんばれ |
breakの位置にreturnやgoto caseを使う
switchステートメントのcase区画の最後で使えるのはbreak;だけではない。
returnは呼び出し元に値を返すことができる。
goto caseは、gotoの一種である。現在のプログラミングではgotoはほぼ使われないため、gotoが使われるのはgoto caseでだけかもしれない。
goto caseを使うと、switch内の別のラベルのcaseに飛ばすことができる。
以下の例ではstatusが”外出中”なので、goto caseによって”離席中”のcaseに飛ばされ、returnによって「今いません」が呼び出し元に返される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var message = GetStatusMessage("外出中"); Console.WriteLine(message); public string GetStatusMessage(string status) { switch (status) { case "在席中": return "います"; case "離席中": case "休憩中": return "今いません"; case "外出中": goto case "離席中"; default: return "不明"; } } |
1 |
今いません |
switchで様々なパターンを使う
タプルパターン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
(int x, int y) p = (2, 3); switch (p) { case (2, 3): Console.WriteLine("にいさんだよ"); break; case (0, 3): Console.WriteLine("おっさんだよ"); break; default: Console.WriteLine("名無しさん"); break; } |
1 |
にいさんだよ |
破棄パターンを加えることで、defaultと同じ効果を得ることができる。つまり、すべてのパターンを網羅することができる。
型パターン
object型はほぼすべての型を格納できる。式の値をobject型にし、型パターンを用いると型に応じて処理を分けることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
object obj = true;//object型にすること switch (obj) { case string s://変数を書くこと Console.WriteLine($"{s.Length}文字です"); break; case int i: Console.WriteLine($"{i}番目"); break; case bool b: Console.WriteLine(b); break; } |
1 |
True |
破棄パターン
パターンにマッチしているか調べるだけで変数への出力は必要ないという場合はアンダースコアを使う。これを破棄パターンという。
1 2 3 4 5 6 7 8 9 10 11 12 |
object obj = "あいうえお"; switch (obj) { case string _://型にマッチしているかどうか調べたいだけならアンスコを使う Console.WriteLine("文字列です"); break; case int _: Console.WriteLine("整数です"); break; } |
1 |
文字列です |
位置指定パターン
位置指定パターン
以下の例ではint型のすべての値にマッチし、その値を変数xに抽出する。これを位置指定パターンという。
位置指定パターンは、パターンを含むパターンである再帰パターンの一例。
1 2 3 4 5 6 7 8 |
int num = 123; switch (num) { case int x: Console.WriteLine(x); break; } |
1 |
123 |
以下はvarを使った例。この場合すべての入力にマッチする。
以下の場合ではxはstring型、yはint型になる。
1 2 3 4 5 6 7 8 |
(string a, int b) tpl2 = ("aaa", 3); switch (tpl2) { case (var x, var y): Console.WriteLine($"{x} {y}"); break; } |
1 |
aaa 3 |
破棄を伴う型パターンと破棄パターン
位置指定パターンで破棄を伴う型パターンと破棄パターンを使う。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
(string x, int y) tpl = ("aaa",3); switch (tpl) { case (var x, _): //_ は何でもよい Console.WriteLine(x); break; } (object a, object b) tpl2 = ("aaa", 3); switch (tpl2) { case (var x, string _): //string _ はstringなら何でもよい Console.WriteLine(x); break; } |
1 2 |
aaa //2つめのswitchはパターンにマッチせず何も表示されない |
プロパティパターン
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 |
string str = "あいうえお"; //プロパティパターンを使う switch (str) { case string { Length: 5 }: Console.WriteLine("長さは5です"); break; } //プロパティパターンを使い、値を変数に抽出する switch (str) { case string { Length: 5 } s: Console.WriteLine($"長さは{s.Length}です"); break; } //プロパティパターンを使い、プロパティの値を変数に抽出する switch (str) { case string { Length: int len }: Console.WriteLine($"長さは{len}です"); break; } //入力の型がはっきりしている場合は型名を省略できる switch (str) { case { Length: int len }: Console.WriteLine($"長さは{len}です"); break; } |
1 2 3 4 |
長さは5です 長さは5です 長さは5です 長さは5です |
whenを使いパターンを限定する
when句を使うことでさらに精度の高いパターンマッチングを指定できる。
when句にはブール式(結果がTrueかFalseになる式)を使うことができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
(int x, int y) point = (15, 15); switch (point) { case (0, 0): Console.WriteLine("てん"); break; case (int x, int y) when x == 0 || y == 0: Console.WriteLine("せん"); break; case (int x, int y) when x > y: Console.WriteLine("長いながしかく"); break; case (int x, int y) when x < y: Console.WriteLine("高いながしかく"); break; case (int x, int y) when x == y: Console.WriteLine("ましかく"); break; } |
1 |
ましかく |
switch式
switch文ではなくswitch式を使うと、caseやbreakを書く必要がなくなり、コンパクトに記述することができる。
また式であるため、メソッドの引数にしたり変数に格納するなど、あらゆる場所に組み込むことができる。
ただし、switch式は値を生成するため、すべてのパターンを記述する必要がある。
またswitch式は1つのパターンにつき1つのステートメントしか書けない。
パターンと処理ステートメントの区切りは=>を使用する。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(int x, int y) point = (15, 15); var shapeName = point switch { (0, 0) => "てん", (int x, int y) when x == 0 || y == 0 => "せん", (int x, int y) when x > y => "長いながしかく", (int x, int y) when x < y => "高いながしかく", (int x, int y) when x == y => "ましかく", _ => throw new InvalidOperationException() }; Console.WriteLine(shapeName); |
1 |
ましかく |
コメント