switch 文は1つの値を定数の case ラベルの一覧と比較し、一致したセクションを実行します。C#では各セクションが break(またはセクションから抜ける別の文)で終わらなければならないので、あるcaseが暗黙のうちに次のcaseへ流れ込むことはありません。
出力:
200: OK
302: redirect
404: not found
500: unhandled status
かっこの中の値は1回だけ評価され、各ラベルと比較されます。default は何にも一致しなかったときに実行されます。省略可能で、一覧のどこに置いてもかまいませんが、通常は最後に置きます。一致するものがなく default もなければ、switch 全体は何もしません。
各セクションにbreakが必要
C、C++、Java、JavaScriptでは、break を忘れると実行が次のcaseへ流れ込みます。C#はその間違いをコンパイルエラーにします。
switch (level)
{
case 1:
Console.WriteLine("read");
// error CS0163: Control cannot fall through from one case label ('case 1:') to another
case 2:
Console.WriteLine("write");
break;
}
ルールは、セクションの文の並びの末尾に到達できてはならない、というものです。通常は break でこれを満たしますが、セクションから抜けるものなら何でも使えます。
returnはメソッド全体から抜けます。小さなヘルパーの中ではこれが最もすっきりした形になることがよくあります。throwは、決して現れないはずの値に対して例外を発生させます。continueは、switchがループの中にあるときに次の反復へ移ります。goto caseとgoto defaultは、意図して別のセクションへジャンプします。
最後のセクションも例外ではありません。末尾の default: に break がないと、CS8070 Control cannot fall out of switch from final case label ('default:') で失敗します。
1つのcaseで複数の値を扱う
間に文がないラベルは、その下のセクションを共有します。空のラベルはコードを隠せないので、これはC#が許す唯一のフォールスルーです。
出力:
'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit
ここでは各セクションが return で終わるので、break は不要です(return の後の break は到達できないコードになります)。同じ値のラベルが2つあるとエラーCS0152 The switch statement contains multiple cases with the label value '1' になり、すべてのラベルはコンパイル時定数でなければなりません。case に変数を書くと「a constant value is expected」で失敗します。
文字列でswitchする
整数の次によく使われるswitchの値は文字列です。比較は序数によるもので大文字と小文字を区別するので、分岐の前に入力を正規化します。
出力:
Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)
ToLowerInvariant がなければ、"STOP" は default に進みます。null の値でswitchするのは正しい構文です。どの文字列定数にも一致せず default に進むので、switch自体が例外を投げることはありません。
enumでswitchする
enumは switch が想定している用途そのもので、名前の付いた値の閉じた集合です。
出力:
Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42
コンパイラは、switch文がenumのすべてのメンバーを網羅しているかを確認しません。また、enum変数は基になる型の任意の整数を保持できます((OrderStatus)42 は正しい構文です)。例外を投げる default を置くと、書き忘れたcaseが、何も起きない静かな不具合ではなく、すぐにわかる明白な失敗になります。
goto caseとgoto default
あるcaseが本当に別のcaseへ続くべきときは、C#ではジャンプを明示的に書きます。
出力:
admin:
manage users
edit posts
read posts
editor:
edit posts
read posts
guest:
read posts
goto case は、同じswitch内のいずれかのラベルに一致する定数を受け取ります。実際のコードではまれで、共通の処理は通常、複数のcaseから呼ぶメソッドにしたほうが明確です。それでも「C#でフォールスルーするには」という問いへの答えはこれです。
case内の変数
switch ブロック全体が1つのスコープなので、2つのcaseで同じ名前の変数を宣言することはできません。
switch (shape)
{
case "square":
int area = side * side;
break;
case "rectangle":
int area = width * height; // error CS0128: A local variable or function named 'area' is already defined in this scope
break;
}
各セクションの本体を波かっこで囲むと、それぞれ独自のスコープになります:case "square": { int area = side * side; ... break; }。
ループの中のbreak
case内の break は switch から抜けるだけで、外側のループからは抜けません。continue はその逆です。switch はループではないので、continue はループの次の反復へ進みます。
出力:
end marker found
Total: 134
ループは "end" の後も続き、99を足しました。switchの中からループを止めるには、フラグを立ててswitchの後で判定するか、ループをメソッドに移して return します。breakとcontinueのページで選択肢を比較しています。
パターンのcaseラベルとwhen
C# 7.0以降、caseラベルには定数の代わりにパターンを書けます。値の型を判定し、when 条件を加えられます。この形は現代的なC#のコードでよく使われます。
static string Describe(object value)
{
switch (value)
{
case null:
return "nothing";
case int n when n < 0:
return $"negative int {n}";
case int n:
return $"int {n}";
case string s when s.Length == 0:
return "empty string";
case string s:
return $"string \"{s}\"";
default:
return $"other: {value.GetType().Name}";
}
}
パターンでは順序が重要です。最初に一致したcaseが採用され、前のcaseがすでに網羅しているために到達できないcaseがあると、コンパイラはエラーを報告します(case int n: を case int n when n < 0: より上に置くと失敗します)。when 句は定数のcaseにも使えます:case 0 when isAdmin:。C# 9で関係パターンと論理パターンが追加され、caseを case >= 90: や case 'a' or 'e' or 'i': と書けるようになりました。
パターンのないC# 7では、同じ型による振り分けは、is 型パターンを使った if の連鎖になります。
出力:
negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing
switchのすべての分岐が値を計算するだけなら、switch式のほうがさらに短く書けます:var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };。switch式のページでは、その構文、パターン、網羅性のルールを扱います。
switchに使える値
| 値の型 | 定数のcase | 補足 |
|---|---|---|
int、long、byte などの整数 | 可 | 最も典型的な用途 |
char | 可 | case 'a': |
string | 可 | 序数比較、大文字と小文字を区別、null は default へ |
bool | 可 | 正しい構文だが、if のほうが明確 |
任意の enum | 可 | 網羅性のチェックなし |
double、decimal | C# 7から | 定数のみ。範囲にはC# 9の関係パターンが必要 |
| その他の任意の型 | C# 7から | 型パターンを通して、case Circle c: |
よくある質問
C#のswitchでbreakは必須ですか?
各caseセクションは、そのセクションから抜ける文で終わらなければなりません。通常は break ですが、return、throw、(ループ内の)continue、goto case でもかまいません。セクションの末尾から次のセクションへ流れ込むとコンパイルエラーCS0163 Control cannot fall through from one case label ('case 1:') to another になり、これは最後のセクションにも当てはまります。
C#のswitchで1つのcaseに複数の値を扱うには?
間に何も書かずにラベルを重ねます:case "sat": case "sun": Console.WriteLine("weekend"); break;。空のcaseラベルは次のラベルへ流れ込んでかまいません。break で終わる必要があるのは、文を含むセクションだけです。C# 9以降では、パターンのcaseで case 1 or 2 or 3: とも書けます。
C#の文字列のswitchは大文字と小文字を区別しますか?
区別します。文字列のcaseは序数による、大文字と小文字を区別する等価比較で比べられるので、"Yes" は case "yes": に一致しません。先に switch (input.Trim().ToLowerInvariant()) のように値を正規化します。null の文字列はどの定数のcaseにも一致せず、default に進みます。
C#のswitchはCやJavaのようにフォールスルーできますか?
暗黙にはできません。あるcaseから別のcaseへ続けたいときは、goto case <value>; や goto default; で明示します。こうするとすべてのフォールスルーがコード上に見えるので、C#は暗黙の形を禁止しています。
C#のswitch文とswitch式の違いは何ですか?
switch文は文を実行し、case、break、default が必要です。switch式(C# 8以降)は値を生成します:var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };。すべての分岐が結果を計算するだけなら式を使います。