C# の switch 文では break によるフォールスルー禁止の代わりに、goto case と goto default という構文で別の case セクションへ明示的にジャンプできます。意外と知られていませんが、この構文は C# 1.0 の時点から言語仕様に含まれています。
なぜ goto case が必要か
C や C++ の switch 文は、break を書かないと次の case に暗黙的にフォールスルーします。
// C 言語の例
switch (x) {
case 1:
doSomething();
// break を書かないと case 2 に落ちる
case 2:
doSomethingElse();
break;
}
C# はこの暗黙フォールスルーをコンパイルエラーとして禁止しました。各 case セクションは break、return、throw、goto のいずれかで終わらなければなりません。
その代わりに、別の case へ明示的に制御を移したいケースのために goto case と goto default が用意されています。
goto case の使い方
switch (foo)
{
case 1:
goto case 2; // case 2 へ制御を移す
case 2:
Console.WriteLine("case 2 の処理");
break;
case 3:
goto default; // default へ制御を移す
default:
Console.WriteLine("default の処理");
break;
}
goto case 値— 指定した値のcaseラベルへジャンプするgoto default—defaultラベルへジャンプする
ジャンプ先の case からは通常どおり処理が続き、最後に break などで switch を抜けます。
従来の goto ラベルとの違い
ユーザーが記憶していた「ラベルへの goto」は以下の形です。
goto MyLabel;
// ...
MyLabel:
Console.WriteLine("ここに飛ぶ");
これはメソッド内のどこへでも飛べる汎用の goto です。switch の外にも飛び出せます。
一方 goto case / goto default は switch 文の中でのみ使える専用構文です。ジャンプ先は同じ switch 内の別の case または default に限られます。
| 構文 | 使える場所 | ジャンプ先 |
|---|---|---|
goto ラベル名; |
どこでも | 同じメソッド内の任意のラベル |
goto case 値; |
switch 文内のみ | 同じ switch 内の指定した case |
goto default; |
switch 文内のみ | 同じ switch 内の default |
実用的な例
複数の値が同じ処理の一部を共有するケースで使われることがあります。
private static decimal CalculatePrice(string plan)
{
decimal price = 0;
switch (plan)
{
case "premium":
price += 500; // premium の追加料金
goto case "basic"; // basic の処理も続けて実行する
case "basic":
price += 1000; // 基本料金
break;
default:
throw new ArgumentException($"不明なプラン: {plan}");
}
return price;
}
Console.WriteLine(CalculatePrice("basic")); // 1000
Console.WriteLine(CalculatePrice("premium")); // 1500
"premium" の場合は追加料金を加算した後、goto case "basic" で基本料金の処理も実行します。共通処理の重複を避けるために使えます。
注意点
goto caseの指定値はコンパイル時定数でなければなりません。変数は使えません。- 存在しない
caseラベルへのgoto caseはコンパイルエラーになります。 - 乱用するとコードの流れが追いにくくなるため、共通処理はメソッドに抽出する方が読みやすいケースも多いです。