C# switch文の使い方を完全解説|基本構文・switch式・case条件・よくあるエラーまでわかる

はじめに

C#で条件分岐を書くとき、最初に思い浮かぶのはif文かもしれません。しかし、1つの値に対して複数の候補を比較する場合は、switch文を使うとコードが読みやすくなります。

たとえば、メニュー番号、ステータスコード、曜日、ユーザー権限、処理状態などを分岐したい場面では、switchが非常に便利です。

C#
int menu = 2;

switch (menu)
{
case 1:
Console.WriteLine("新規作成");
break;
case 2:
Console.WriteLine("編集");
break;
case 3:
Console.WriteLine("削除");
break;
default:
Console.WriteLine("不明なメニューです");
break;
}

C#のswitchは、単純な値の比較だけでなく、文字列、enum、型判定、範囲条件、when句、パターンマッチング、switch式など、さまざまな書き方に対応しています。Microsoft公式ドキュメントでも、switch文とswitch式はパターンマッチングを扱う主要な構文として説明されています。

この記事では、C#のswitch文の基本構文から、switch式case条件、breakreturnの使い分け、よくあるエラー、実践的な使い方まで順番に解説します。

1. C#のswitch文とは

C#のswitch文とは、ある値や式の結果に応じて、実行する処理を切り替えるための条件分岐構文です。

たとえば、「変数の値が1ならAの処理、2ならBの処理、3ならCの処理」といった分岐を、見やすく整理して書けます。

C#
string role = "Admin";

switch (role)
{
case "Admin":
Console.WriteLine("管理者です");
break;
case "User":
Console.WriteLine("一般ユーザーです");
break;
case "Guest":
Console.WriteLine("ゲストです");
break;
default:
Console.WriteLine("不明な権限です");
break;
}

switch文は、複数の条件を上から順に評価し、最初に一致したcaseの処理を実行します。caseに一致しない場合は、defaultがあればその処理が実行されます。

1-1. switch文でできること

C#のswitch文では、主に次のようなことができます。

1つの値に対して複数の候補を比較できます。

C#
int number = 1;

switch (number)
{
case 1:
Console.WriteLine("one");
break;
case 2:
Console.WriteLine("two");
break;
}

文字列を使った分岐もできます。

C#
string command = "start";

switch (command)
{
case "start":
Console.WriteLine("開始します");
break;
case "stop":
Console.WriteLine("停止します");
break;
}

enumと組み合わせて、状態や種類をわかりやすく分岐できます。

C#
enum OrderStatus
{
Pending,
Shipped,
Delivered,
Canceled
}

OrderStatus status = OrderStatus.Shipped;

switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("注文確認中です");
break;
case OrderStatus.Shipped:
Console.WriteLine("発送済みです");
break;
case OrderStatus.Delivered:
Console.WriteLine("配達完了です");
break;
case OrderStatus.Canceled:
Console.WriteLine("キャンセル済みです");
break;
}

さらに、C#の新しい構文では、範囲条件や型による分岐も書けます。

C#
int score = 85;

switch (score)
{
case >= 90:
Console.WriteLine("A");
break;
case >= 80:
Console.WriteLine("B");
break;
case >= 70:
Console.WriteLine("C");
break;
default:
Console.WriteLine("D");
break;
}

1-2. if文・else if文との違い

if文は、自由な条件式を書ける汎用的な条件分岐です。

C#
if (score >= 90)
{
Console.WriteLine("A");
}
else if (score >= 80)
{
Console.WriteLine("B");
}
else
{
Console.WriteLine("C");
}

一方、switch文は「1つの対象を複数のパターンと照合する」場面に向いています。

C#
switch (score)
{
case >= 90:
Console.WriteLine("A");
break;
case >= 80:
Console.WriteLine("B");
break;
default:
Console.WriteLine("C");
break;
}

if文は複雑な条件を柔軟に書けますが、条件が増えると読みづらくなりやすいです。switch文は、条件の対象が明確な場合に、分岐を一覧化しやすいのが特徴です。

たとえば、次のようなelse ifが長く続くコードは、switchに置き換えると見通しがよくなります。

C#
if (command == "create")
{
Create();
}
else if (command == "update")
{
Update();
}
else if (command == "delete")
{
Delete();
}
else
{
ShowError();
}

switchで書くと次のようになります。

C#
switch (command)
{
case "create":
Create();
break;
case "update":
Update();
break;
case "delete":
Delete();
break;
default:
ShowError();
break;
}

1-3. switch文を使うべきケース

switch文は、次のようなケースで使うと効果的です。

値の候補が決まっている場合は、switchが向いています。

C#
switch (day)
{
case "Monday":
Console.WriteLine("月曜日");
break;
case "Tuesday":
Console.WriteLine("火曜日");
break;
case "Wednesday":
Console.WriteLine("水曜日");
break;
}

enumの値に応じて処理を分ける場合も、switchが適しています。

C#
switch (status)
{
case OrderStatus.Pending:
ConfirmOrder();
break;
case OrderStatus.Shipped:
TrackShipment();
break;
case OrderStatus.Canceled:
ShowCancelReason();
break;
}

メニューやコマンドを分岐する処理にも使いやすいです。

C#
switch (input)
{
case "1":
ShowList();
break;
case "2":
AddItem();
break;
case "3":
DeleteItem();
break;
default:
Console.WriteLine("正しい番号を入力してください");
break;
}

条件が「同じ変数に対する比較」で統一されているなら、ifよりswitchのほうが読みやすくなることが多いです。

1-4. switch文を使わないほうがよいケース

一方で、すべての条件分岐をswitchで書けばよいわけではありません。

複雑な条件が組み合わさっている場合は、if文のほうが自然です。

C#
if (age >= 20 && hasLicense && !isExpired)
{
Console.WriteLine("利用できます");
}

このように、複数の変数を使った条件や、ビジネスルールが複雑な条件では、無理にswitchにすると逆に読みづらくなります。

また、caseが非常に多くなり、各処理が大きくなる場合も注意が必要です。

C#
switch (type)
{
case "A":
// 長い処理
break;
case "B":
// 長い処理
break;
case "C":
// 長い処理
break;
}

このような場合は、メソッドに分割したり、辞書、ポリモーフィズム、Strategyパターンなどを検討したほうがよいことがあります。

2. C# switch文の基本構文

C#のswitch文の基本構文は次のとおりです。

C#
switch ()
{
case 値1:
// 値1に一致したときの処理
break;

case 値2:
// 値2に一致したときの処理
break;

default:
// どのcaseにも一致しなかったときの処理
break;
}

switchの丸括弧内に判定したい値や式を書き、caseごとに一致したときの処理を書きます。

2-1. switch・case・break・defaultの役割

switchは、分岐の対象となる値を指定するキーワードです。

C#
switch (number)

caseは、対象の値と比較する候補を指定します。

C#
case 1:

breakは、現在のswitch文から抜けるために使います。

C#
break;

defaultは、どのcaseにも一致しなかった場合に実行されます。

C#
default:
Console.WriteLine("該当なし");
break;

基本的には、各caseの最後にbreakを書くと覚えておけば問題ありません。ただし、returnthrowで処理を終了する場合は、breakを書かなくても構いません。

2-2. 数値で分岐する基本例

数値で分岐する例を見てみましょう。

C#
int rank = 2;

switch (rank)
{
case 1:
Console.WriteLine("金メダル");
break;
case 2:
Console.WriteLine("銀メダル");
break;
case 3:
Console.WriteLine("銅メダル");
break;
default:
Console.WriteLine("入賞外");
break;
}

この場合、rank2なので、次の処理が実行されます。

C#
Console.WriteLine("銀メダル");

数値によるswitchは、メニュー番号、ステータスコード、分類IDなどを処理するときによく使われます。

2-3. 文字列で分岐する基本例

C#のswitch文では、文字列を使った分岐もできます。

C#
string command = "save";

switch (command)
{
case "new":
Console.WriteLine("新規作成します");
break;
case "save":
Console.WriteLine("保存します");
break;
case "exit":
Console.WriteLine("終了します");
break;
default:
Console.WriteLine("不明なコマンドです");
break;
}

文字列のswitchは、ユーザー入力、APIのパラメータ、コマンド名などを判定するときに便利です。

ただし、文字列比較では大文字・小文字の違いに注意が必要です。

C#
string command = "Save";

switch (command)
{
case "save":
Console.WriteLine("保存します");
break;
default:
Console.WriteLine("一致しません");
break;
}

この場合、"Save""save"は別の文字列なので一致しません。大文字・小文字を無視したい場合は、事前に変換しておく方法があります。

C#
string command = "Save";

switch (command.ToLower())
{
case "save":
Console.WriteLine("保存します");
break;
}

ただし、commandnullの可能性がある場合、ToLower()を呼ぶとNullReferenceExceptionが発生します。安全に書くなら、次のようにします。

C#
switch (command?.ToLower())
{
case "save":
Console.WriteLine("保存します");
break;
default:
Console.WriteLine("不明なコマンドです");
break;
}

2-4. enumで分岐する基本例

enumは、決まった値の集合を表す型です。switchと非常に相性がよく、状態管理や種別判定でよく使われます。

C#
enum PaymentStatus
{
Unpaid,
Paid,
Refunded
}

このenumswitchで分岐してみます。

C#
PaymentStatus status = PaymentStatus.Paid;

switch (status)
{
case PaymentStatus.Unpaid:
Console.WriteLine("未払いです");
break;
case PaymentStatus.Paid:
Console.WriteLine("支払い済みです");
break;
case PaymentStatus.Refunded:
Console.WriteLine("返金済みです");
break;
default:
Console.WriteLine("不明な支払い状態です");
break;
}

enumを使うと、12のような意味のわかりにくい数値ではなく、PaymentStatus.Paidのように意味のある名前で分岐できます。

これは、可読性と保守性の向上につながります。

2-5. defaultを省略できるケースと書くべきケース

defaultは必須ではありません。次のように省略できます。

C#
switch (status)
{
case PaymentStatus.Unpaid:
Console.WriteLine("未払いです");
break;
case PaymentStatus.Paid:
Console.WriteLine("支払い済みです");
break;
}

ただし、実務ではdefaultを書いたほうがよいケースが多いです。

たとえば、想定外の値が入ったときにログを出したり、例外を投げたりできます。

C#
switch (status)
{
case PaymentStatus.Unpaid:
Console.WriteLine("未払いです");
break;
case PaymentStatus.Paid:
Console.WriteLine("支払い済みです");
break;
case PaymentStatus.Refunded:
Console.WriteLine("返金済みです");
break;
default:
throw new InvalidOperationException("不明な支払い状態です");
}

特にenumを使っている場合でも、キャストによって定義外の値が入る可能性があります。

C#
PaymentStatus status = (PaymentStatus)999;

そのため、想定外の値を検出したい場合はdefaultを書くのがおすすめです。

一方で、何もしないことが明確な場合は、defaultを省略しても問題ありません。ただし、「書き忘れ」なのか「意図的な省略」なのかがわかるように、コメントを書くと親切です。

C#
switch (status)
{
case PaymentStatus.Paid:
SendReceipt();
break;

// その他の状態では処理しない
}

3. caseの書き方と条件指定

C#のcaseには、単純な値だけでなく、パターンや条件を指定できます。

基本形は次のとおりです。

C#
switch (value)
{
case 条件:
// 処理
break;
}

以前のC#では、caseに書ける内容は定数値が中心でした。しかし現在のC#では、パターンマッチングにより、型、範囲、プロパティ、条件付き分岐なども扱えます。C#のパターンには、型パターン、定数パターン、関係パターン、プロパティパターン、タプルパターン、discardパターンなどがあります。

3-1. caseに指定できる値

基本的なcaseには、数値、文字、文字列、enumなどを指定できます。

C#
int value = 10;

switch (value)
{
case 0:
Console.WriteLine("ゼロ");
break;
case 10:
Console.WriteLine("十");
break;
}

文字も指定できます。

C#
char grade = 'A';

switch (grade)
{
case 'A':
Console.WriteLine("優秀");
break;
case 'B':
Console.WriteLine("良好");
break;
}

文字列も指定できます。

C#
string color = "red";

switch (color)
{
case "red":
Console.WriteLine("赤");
break;
case "blue":
Console.WriteLine("青");
break;
}

enumも指定できます。

C#
switch (status)
{
case PaymentStatus.Paid:
Console.WriteLine("支払い済み");
break;
}

さらに、nullcaseで判定できます。

C#
string? name = null;

switch (name)
{
case null:
Console.WriteLine("名前がありません");
break;
default:
Console.WriteLine(name);
break;
}

3-2. 複数のcaseで同じ処理を実行する方法

複数のcaseで同じ処理をしたい場合は、caseを連続して書きます。

C#
int month = 1;

switch (month)
{
case 12:
case 1:
case 2:
Console.WriteLine("冬です");
break;

case 3:
case 4:
case 5:
Console.WriteLine("春です");
break;

case 6:
case 7:
case 8:
Console.WriteLine("夏です");
break;

case 9:
case 10:
case 11:
Console.WriteLine("秋です");
break;

default:
Console.WriteLine("正しい月ではありません");
break;
}

この書き方では、1212のいずれかに一致した場合、同じ処理が実行されます。

C#では、処理を持たない空のcaseを連続させることはできます。ただし、処理を書いたcaseから次のcaseへそのまま流れるフォールスルーは制限されています。

3-3. caseで範囲条件を指定する方法

C#の関係パターンを使うと、caseで範囲条件を書けます。

C#
int score = 82;

switch (score)
{
case >= 90:
Console.WriteLine("A");
break;
case >= 80:
Console.WriteLine("B");
break;
case >= 70:
Console.WriteLine("C");
break;
case >= 60:
Console.WriteLine("D");
break;
default:
Console.WriteLine("F");
break;
}

この例では、score82なので、case >= 80:に一致します。

範囲を明確に書きたい場合は、andを使えます。

C#
int score = 75;

switch (score)
{
case >= 80 and <= 100:
Console.WriteLine("高得点");
break;
case >= 60 and < 80:
Console.WriteLine("合格");
break;
case >= 0 and < 60:
Console.WriteLine("不合格");
break;
default:
Console.WriteLine("不正な点数です");
break;
}

andornotを使った論理パターンにより、複数の条件を組み合わせて書けます。

3-4. when句で条件を追加する方法

when句を使うと、caseに追加条件を付けられます。

C#
int score = 95;
bool isAttendanceGood = true;

switch (score)
{
case >= 90 when isAttendanceGood:
Console.WriteLine("評価A");
break;
case >= 90:
Console.WriteLine("点数は高いですが、出席状況を確認してください");
break;
default:
Console.WriteLine("通常評価です");
break;
}

case >= 90 when isAttendanceGood:は、「scoreが90以上」かつ「isAttendanceGoodがtrue」の場合に一致します。

when句は、型パターンと組み合わせることもできます。

C#
object value = "hello";

switch (value)
{
case string text when text.Length >= 5:
Console.WriteLine("5文字以上の文字列です");
break;
case string text:
Console.WriteLine("短い文字列です");
break;
default:
Console.WriteLine("文字列ではありません");
break;
}

whenを使うと柔軟な条件分岐ができますが、条件が複雑になりすぎると読みづらくなります。複雑な判定はメソッドに切り出すとよいでしょう。

C#
switch (user)
{
case User u when CanAccessAdminPage(u):
Console.WriteLine("管理画面にアクセスできます");
break;
}

3-5. null判定をswitchで行う方法

switchでは、nullを明示的に判定できます。

C#
string? message = null;

switch (message)
{
case null:
Console.WriteLine("メッセージがありません");
break;
case "":
Console.WriteLine("空文字です");
break;
default:
Console.WriteLine(message);
break;
}

nullと空文字は別物です。

C#
string? a = null;
string b = "";

nullは値が存在しない状態、空文字は長さ0の文字列です。そのため、必要に応じて別々に判定します。

when句を使って、空白文字も含めて判定することもできます。

C#
string? input = "   ";

switch (input)
{
case null:
Console.WriteLine("nullです");
break;
case string s when string.IsNullOrWhiteSpace(s):
Console.WriteLine("空白です");
break;
default:
Console.WriteLine("入力があります");
break;
}

ただし、この例ではcase nullを先に書いています。string.IsNullOrWhiteSpace自体はnullも扱えますが、switchの分岐を読みやすくするためにnullを明示するのはよい書き方です。

3-6. defaultとcase _の違い

defaultcase _は、どちらも「その他」を扱うように見えますが、意味と評価のされ方に違いがあります。

defaultは、どのcaseにも一致しなかった場合に実行されます。

C#
switch (value)
{
case 1:
Console.WriteLine("1です");
break;
default:
Console.WriteLine("その他です");
break;
}

一方、case _はdiscardパターンで、「任意の値に一致するパターン」です。

C#
switch (value)
{
case 1:
Console.WriteLine("1です");
break;
case _:
Console.WriteLine("その他です");
break;
}

この例では、実質的にdefaultと同じように使えます。

ただし、case _は通常のcaseと同じく、上から順に評価されます。そのため、先頭に書くと後続のcaseに到達できなくなります。

C#
switch (value)
{
case _:
Console.WriteLine("すべてここに入ります");
break;

// このcaseは到達不能になります
case 1:
Console.WriteLine("1です");
break;
}

switch文で「どれにも一致しない場合」を表したいだけなら、基本的にはdefaultを使うと読みやすいです。

一方、switch式ではdefaultではなく_を使うのが一般的です。

C#
string result = value switch
{
1 => "1です",
_ => "その他です"
};

4. break・return・throw・gotoの使い方

C#のswitch文では、各caseの処理をどのように終了させるかが重要です。よく使うのはbreakですが、状況によってはreturnthrowgoto casegoto defaultも使えます。

4-1. breakが必要な理由

breakは、switch文から抜けるためのキーワードです。

C#
int value = 1;

switch (value)
{
case 1:
Console.WriteLine("1です");
break;
case 2:
Console.WriteLine("2です");
break;
}

case 1に一致したあと、breakによってswitch文の外へ抜けます。

C#では、処理があるcaseから次のcaseへそのまま流れるフォールスルーは基本的に許可されていません。そのため、breakを書き忘れるとコンパイルエラーになります。

C#
switch (value)
{
case 1:
Console.WriteLine("1です");
case 2:
Console.WriteLine("2です");
break;
}

このようなコードはエラーになります。

C言語などでは、breakを書き忘れると次のcaseまで実行されることがあります。しかしC#では、それによるバグを防ぐため、明示的に終了処理を書く必要があります。

4-2. returnでswitchを抜ける書き方

メソッドの中でswitchを使っている場合、returnでメソッド自体を終了できます。

C#
string GetDayName(int day)
{
switch (day)
{
case 1:
return "月曜日";
case 2:
return "火曜日";
case 3:
return "水曜日";
default:
return "不明な曜日";
}
}

この場合、各casereturnしているため、breakは不要です。

値を返す処理では、breakよりreturnのほうがシンプルに書けることがあります。

C#
bool IsWeekend(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
return true;
default:
return false;
}
}

ただし、処理の途中で何度もreturnすると、流れが追いにくくなることもあります。短いメソッドでは便利ですが、長いメソッドでは注意しましょう。

4-3. throwで例外を投げる書き方

想定外の値が来た場合は、throwで例外を投げることがあります。

C#
string GetStatusMessage(PaymentStatus status)
{
switch (status)
{
case PaymentStatus.Unpaid:
return "未払い";
case PaymentStatus.Paid:
return "支払い済み";
case PaymentStatus.Refunded:
return "返金済み";
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "不明なステータスです");
}
}

defaultで例外を投げておくと、予期しない値が入ったときに問題を早く発見できます。

特にenumでは、定義されていない値がキャストで入ることがあります。

C#
PaymentStatus status = (PaymentStatus)999;

このような値を静かに無視すると、後から原因を追いにくいバグになります。想定外の値を許容しない設計なら、defaultで例外を投げるのが有効です。

4-4. goto case・goto defaultの使い方

C#のswitchでは、goto casegoto defaultを使って、別のcaseへ明示的に移動できます。

C#
int value = 1;

switch (value)
{
case 1:
Console.WriteLine("1の処理");
goto case 2;

case 2:
Console.WriteLine("2の処理");
break;

default:
Console.WriteLine("その他");
break;
}

このコードでは、case 1の処理を実行したあと、case 2の処理に移動します。

goto defaultも使えます。

C#
switch (value)
{
case 1:
Console.WriteLine("1の処理");
goto default;

default:
Console.WriteLine("共通処理");
break;
}

ただし、goto casegoto defaultは多用しないほうがよいです。処理の流れが複雑になり、読みづらくなるためです。

複数のcaseで同じ処理をしたいだけなら、次のようにcaseを並べるほうが自然です。

C#
switch (value)
{
case 1:
case 2:
Console.WriteLine("1または2です");
break;
}

共通処理があるなら、メソッドに切り出す方法もおすすめです。

C#
switch (value)
{
case 1:
DoCommon();
DoA();
break;
case 2:
DoCommon();
DoB();
break;
}

4-5. C#でフォールスルーが制限される理由

フォールスルーとは、あるcaseの処理が終わったあと、次のcaseへそのまま流れて実行されることです。

C言語などでは、breakを書き忘れると意図せず次のcaseが実行されることがあります。

C
switch (value)
{
case 1:
printf("1");
case 2:
printf("2");
break;
}

このような挙動は、バグの原因になりやすいです。

C#では、処理を持つcaseから次のcaseへ暗黙的に流れることを制限しています。そのため、breakreturnthrowgotoなどで、どこへ制御を移すのか明示する必要があります。

C#
switch (value)
{
case 1:
Console.WriteLine("1");
break;

case 2:
Console.WriteLine("2");
break;
}

この仕様により、breakの書き忘れによる予期しない実行を防ぎやすくなっています。

5. C#のswitch式の使い方

C#には、従来のswitch文に加えて、値を返すためのswitch式があります。

switch文は「処理を分岐する」ために使います。

C#
switch (status)
{
case 1:
Console.WriteLine("有効");
break;
case 2:
Console.WriteLine("無効");
break;
}

一方、switch式は「分岐結果として値を返す」ために使います。

C#
string message = status switch
{
1 => "有効",
2 => "無効",
_ => "不明"
};

switch式は、値の変換、メッセージ生成、分類処理などで非常に便利です。Microsoft公式ドキュメントでも、switch式は入力式に対するパターンマッチに基づいて1つの式を評価する構文として説明されています。

5-1. switch文とswitch式の違い

switch文switch式の大きな違いは、文か式かです。

switch文は処理を実行します。

C#
switch (level)
{
case 1:
Console.WriteLine("初心者");
break;
case 2:
Console.WriteLine("中級者");
break;
default:
Console.WriteLine("不明");
break;
}

switch式は値を返します。

C#
string levelName = level switch
{
1 => "初心者",
2 => "中級者",
_ => "不明"
};

switch文ではcasebreakdefaultを使います。

switch式ではcasebreakは使わず、=>,を使います。また、その他の値を表すときはdefaultではなく_を使います。

switch式は短く書けますが、複雑な処理を詰め込みすぎると読みにくくなります。単純な変換処理にはswitch式、複数行の処理にはswitch文という使い分けが基本です。

5-2. switch式の基本構文

switch式の基本構文は次のとおりです。

C#
var result = value switch
{
パターン1 => 結果1,
パターン2 => 結果2,
_ => デフォルト結果
};

具体例を見てみましょう。

C#
int code = 200;

string message = code switch
{
200 => "OK",
400 => "Bad Request",
404 => "Not Found",
500 => "Internal Server Error",
_ => "Unknown"
};

Console.WriteLine(message);

code200なので、messageには"OK"が入ります。

switch式の各行は「switchアーム」と呼ばれます。各アームは、パターン、=>、結果の式で構成されます。

C#
200 => "OK"

最後のアームにも,を付けることが多いです。

C#
_ => "Unknown",

最後の,は省略できますが、後から行を追加しやすくなるため、付けておく書き方がよく使われます。

5-3. 値を返すswitch式のサンプル

switch式は、値の変換処理で特に便利です。

C#
DayOfWeek day = DayOfWeek.Monday;

string japanese = day switch
{
DayOfWeek.Monday => "月曜日",
DayOfWeek.Tuesday => "火曜日",
DayOfWeek.Wednesday => "水曜日",
DayOfWeek.Thursday => "木曜日",
DayOfWeek.Friday => "金曜日",
DayOfWeek.Saturday => "土曜日",
DayOfWeek.Sunday => "日曜日",
_ => "不明"
};

メソッドの戻り値として直接返すこともできます。

C#
string GetDayName(DayOfWeek day)
{
return day switch
{
DayOfWeek.Monday => "月曜日",
DayOfWeek.Tuesday => "火曜日",
DayOfWeek.Wednesday => "水曜日",
DayOfWeek.Thursday => "木曜日",
DayOfWeek.Friday => "金曜日",
DayOfWeek.Saturday => "土曜日",
DayOfWeek.Sunday => "日曜日",
_ => "不明"
};
}

switch文で書くよりも短く、変換の対応関係が見やすくなります。

5-4. ラムダ風の=>を使う書き方

switch式では、=>を使って「このパターンならこの値を返す」と書きます。

C#
string result = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
_ => "D"
};

=>の右側には式を書きます。

C#
int point = rank switch
{
1 => 100,
2 => 80,
3 => 60,
_ => 0
};

メソッド呼び出しも書けます。

C#
string message = status switch
{
PaymentStatus.Unpaid => CreateUnpaidMessage(),
PaymentStatus.Paid => CreatePaidMessage(),
PaymentStatus.Refunded => CreateRefundedMessage(),
_ => CreateUnknownMessage()
};

ただし、=>の右側に複数行の命令文をそのまま書くことはできません。複雑な処理が必要な場合は、メソッドに切り出すか、switch文を使いましょう。

5-5. discardパターン「_」の使い方

switch式では、どのパターンにも一致しなかった場合を表すために_をよく使います。

C#
string message = code switch
{
200 => "成功",
400 => "リクエストエラー",
404 => "見つかりません",
_ => "不明なステータス"
};

_はdiscardパターンと呼ばれ、任意の値に一致します。

_を書かない場合、すべての可能性を網羅できていないと警告が出ることがあります。また、実行時にどのアームにも一致しない場合、例外が発生する可能性があります。

C#
string message = code switch
{
200 => "成功",
400 => "エラー"
};

このコードでcode500だった場合、該当するアームがありません。安全に書くためには、通常は_を用意します。

C#
string message = code switch
{
200 => "成功",
400 => "エラー",
_ => "不明"
};

ただし、想定外の値を明確にエラーにしたい場合は、_で例外を投げるのもよい方法です。

5-6. switch式で例外を投げる方法

switch式でも、throw式を使って例外を投げられます。

C#
string GetStatusMessage(PaymentStatus status)
{
return status switch
{
PaymentStatus.Unpaid => "未払い",
PaymentStatus.Paid => "支払い済み",
PaymentStatus.Refunded => "返金済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, "不明なステータスです")
};
}

この書き方は、enumの変換処理でよく使われます。

_ => "不明"とするか、_ => throw ...とするかは、設計によって決めます。

画面表示などで不明な値をそのまま扱いたい場合は、文字列を返してもよいでしょう。

C#
_ => "不明"

一方、システム上ありえない値なら、例外を投げて早く検知したほうが安全です。

C#
_ => throw new InvalidOperationException("想定外の値です")

5-7. switch式を使うとコードが短くなる例

従来のswitch文で値を返す処理を書くと、次のようになります。

C#
string GetRankName(int rank)
{
switch (rank)
{
case 1:
return "ゴールド";
case 2:
return "シルバー";
case 3:
return "ブロンズ";
default:
return "ノーマル";
}
}

switch式で書くと、より短くなります。

C#
string GetRankName(int rank)
{
return rank switch
{
1 => "ゴールド",
2 => "シルバー",
3 => "ブロンズ",
_ => "ノーマル"
};
}

さらに、式形式のメソッドにすればもっと簡潔にできます。

C#
string GetRankName(int rank) => rank switch
{
1 => "ゴールド",
2 => "シルバー",
3 => "ブロンズ",
_ => "ノーマル"
};

このように、単純な値変換ではswitch式を使うとコードが短くなり、対応関係も見やすくなります。

ただし、処理が複雑な場合は無理に短くしないことが大切です。読みやすさを優先して、switch文やメソッド分割を選びましょう。

6. パターンマッチングを使ったswitch

C#のswitchは、パターンマッチングと組み合わせることで、単純な値比較以上の分岐ができます。

パターンマッチングとは、値が特定の形や条件に合っているかを判定する仕組みです。C#では、型パターン、プロパティパターン、タプルパターン、関係パターン、論理パターンなどを使えます。

6-1. 型パターンで分岐する方法

型パターンを使うと、オブジェクトの実際の型によって分岐できます。

C#
object value = "Hello";

switch (value)
{
case string text:
Console.WriteLine($"文字列です: {text}");
break;
case int number:
Console.WriteLine($"整数です: {number}");
break;
case bool flag:
Console.WriteLine($"真偽値です: {flag}");
break;
case null:
Console.WriteLine("nullです");
break;
default:
Console.WriteLine("その他の型です");
break;
}

case string text:は、valuestring型なら一致し、その値をtextという変数として使えます。

型判定とキャストを同時にできるため、次のような古い書き方より簡潔です。

C#
if (value is string)
{
string text = (string)value;
Console.WriteLine(text);
}

型パターンを使うと、次のように書けます。

C#
if (value is string text)
{
Console.WriteLine(text);
}

switchと組み合わせると、複数の型に対する処理を整理できます。

6-2. プロパティパターンで分岐する方法

プロパティパターンを使うと、オブジェクトのプロパティの値で分岐できます。

C#
class User
{
public string Role { get; set; } = "";
public bool IsActive { get; set; }
}

このUserswitchで判定してみます。

C#
User user = new User { Role = "Admin", IsActive = true };

switch (user)
{
case { Role: "Admin", IsActive: true }:
Console.WriteLine("有効な管理者です");
break;
case { Role: "Admin", IsActive: false }:
Console.WriteLine("無効な管理者です");
break;
case { Role: "User", IsActive: true }:
Console.WriteLine("有効な一般ユーザーです");
break;
default:
Console.WriteLine("その他のユーザーです");
break;
}

プロパティパターンを使うと、ifでプロパティを何度も参照するより、条件を構造的に書けます。

C#
string message = user switch
{
{ Role: "Admin", IsActive: true } => "有効な管理者",
{ Role: "Admin", IsActive: false } => "無効な管理者",
{ Role: "User", IsActive: true } => "有効なユーザー",
_ => "その他"
};

ネストしたプロパティも判定できます。

C#
class Order
{
public Customer Customer { get; set; } = new Customer();
}

class Customer
{
public string Rank { get; set; } = "";
}

string discount = order switch
{
{ Customer: { Rank: "Gold" } } => "20%割引",
{ Customer: { Rank: "Silver" } } => "10%割引",
_ => "割引なし"
};

6-3. タプルパターンで複数値を分岐する方法

タプルパターンを使うと、複数の値の組み合わせで分岐できます。

C#
bool isMember = true;
int totalPrice = 12000;

string rank = (isMember, totalPrice) switch
{
(true, >= 10000) => "会員特別割引",
(true, >= 5000) => "会員割引",
(false, >= 10000) => "通常高額購入",
_ => "通常購入"
};

この例では、isMembertotalPriceの2つの値を同時に判定しています。

switch文でもタプルを使えます。

C#
switch (isMember, totalPrice)
{
case (true, >= 10000):
Console.WriteLine("会員特別割引");
break;
case (true, >= 5000):
Console.WriteLine("会員割引");
break;
case (false, >= 10000):
Console.WriteLine("通常高額購入");
break;
default:
Console.WriteLine("通常購入");
break;
}

タプルパターンは、複数の条件を組み合わせるときに便利です。

たとえば、座標の位置を判定できます。

C#
int x = 0;
int y = 5;

string position = (x, y) switch
{
(0, 0) => "原点",
(0, _) => "Y軸上",
(_, 0) => "X軸上",
(> 0, > 0) => "第1象限",
(< 0, > 0) => "第2象限",
(< 0, < 0) => "第3象限",
(> 0, < 0) => "第4象限"
};

6-4. 関係パターンで大小比較をする方法

関係パターンを使うと、>>=<<=による比較をcaseswitch式で書けます。

C#
int temperature = 32;

string message = temperature switch
{
>= 35 => "猛暑日です",
>= 30 => "真夏日です",
>= 25 => "夏日です",
< 0 => "氷点下です",
_ => "通常の気温です"
};

switch文でも同じように書けます。

C#
switch (temperature)
{
case >= 35:
Console.WriteLine("猛暑日です");
break;
case >= 30:
Console.WriteLine("真夏日です");
break;
case >= 25:
Console.WriteLine("夏日です");
break;
case < 0:
Console.WriteLine("氷点下です");
break;
default:
Console.WriteLine("通常の気温です");
break;
}

関係パターンでは、順番が重要です。

C#
int score = 95;

string grade = score switch
{
>= 60 => "合格",
>= 90 => "高得点",
_ => "不合格"
};

このコードでは、95は先に>= 60に一致してしまうため、>= 90には到達しません。正しくは、条件の狭いものを先に書きます。

C#
string grade = score switch
{
>= 90 => "高得点",
>= 60 => "合格",
_ => "不合格"
};

6-5. 論理パターンand・or・notの使い方

論理パターンを使うと、複数のパターンを組み合わせられます。

andは、両方の条件を満たす場合に一致します。

C#
int age = 25;

string result = age switch
{
>= 20 and < 65 => "成人",
>= 65 => "高齢者",
< 20 => "未成年"
};

orは、どちらかの条件に一致すれば一致します。

C#
DayOfWeek day = DayOfWeek.Saturday;

string type = day switch
{
DayOfWeek.Saturday or DayOfWeek.Sunday => "休日",
_ => "平日"
};

notは、条件に一致しない場合を表します。

C#
string role = "User";

string access = role switch
{
not "Guest" => "アクセス可能",
_ => "ゲストは制限されています"
};

ただし、notを使うときは読みやすさに注意が必要です。否定条件が増えると、コードの意図がわかりにくくなることがあります。

範囲外を判定する場合は、次のように書けます。

C#
int score = 120;

string message = score switch
{
< 0 or > 100 => "不正な点数です",
>= 80 => "良い点数です",
_ => "通常の点数です"
};

6-6. switch文とパターンマッチングを組み合わせる実践例

実践的な例として、注文情報を判定する処理を考えてみます。

C#
class Order
{
public string Status { get; set; } = "";
public int TotalPrice { get; set; }
public bool IsPriority { get; set; }
}

switch文で判定します。

C#
Order order = new Order
{
Status = "Paid",
TotalPrice = 15000,
IsPriority = true
};

switch (order)
{
case { Status: "Paid", TotalPrice: >= 10000, IsPriority: true }:
Console.WriteLine("優先発送の高額注文です");
break;

case { Status: "Paid", TotalPrice: >= 10000 }:
Console.WriteLine("高額注文です");
break;

case { Status: "Paid" }:
Console.WriteLine("通常の支払い済み注文です");
break;

case { Status: "Canceled" }:
Console.WriteLine("キャンセル済み注文です");
break;

default:
Console.WriteLine("確認が必要な注文です");
break;
}

このように、プロパティパターンや関係パターンを組み合わせると、オブジェクトの状態に応じた分岐をわかりやすく書けます。

同じ処理をswitch式で書くと、メッセージ生成をシンプルにできます。

C#
string message = order switch
{
{ Status: "Paid", TotalPrice: >= 10000, IsPriority: true } => "優先発送の高額注文です",
{ Status: "Paid", TotalPrice: >= 10000 } => "高額注文です",
{ Status: "Paid" } => "通常の支払い済み注文です",
{ Status: "Canceled" } => "キャンセル済み注文です",
_ => "確認が必要な注文です"
};

処理を実行するならswitch文、値を返すならswitch式を使うとよいでしょう。

7. C# switchでよくあるエラーと解決方法

C#のswitchは便利ですが、いくつかのエラーに遭遇しやすい構文でもあります。

特によくあるのは、breakの書き忘れ、caseの重複、到達不能コード、switch式の網羅性不足、nullの考慮漏れです。

7-1. 「Control cannot fall through from one case label」の原因

C#でswitchを書いていると、次のようなエラーが出ることがあります。

Control cannot fall through from one case label

これは、あるcaseから次のcaseへ処理がそのまま流れてしまう可能性があるときに発生します。

C#
int value = 1;

switch (value)
{
case 1:
Console.WriteLine("1です");

case 2:
Console.WriteLine("2です");
break;
}

case 1の最後にbreakがないため、C#ではエラーになります。

解決方法は、breakを追加することです。

C#
switch (value)
{
case 1:
Console.WriteLine("1です");
break;

case 2:
Console.WriteLine("2です");
break;
}

または、メソッド内ならreturnでも構いません。

C#
string GetMessage(int value)
{
switch (value)
{
case 1:
return "1です";
case 2:
return "2です";
default:
return "その他です";
}
}

7-2. breakを書き忘れたときの対処法

breakを書き忘れた場合は、基本的に各caseの最後にbreakを追加します。

C#
switch (command)
{
case "start":
Start();
break;

case "stop":
Stop();
break;

default:
ShowError();
break;
}

ただし、すべてのケースでbreakが必要なわけではありません。

returnする場合は不要です。

C#
switch (command)
{
case "start":
return "開始";
case "stop":
return "停止";
default:
return "不明";
}

throwする場合も不要です。

C#
switch (command)
{
case "start":
Start();
break;

default:
throw new ArgumentException("不明なコマンドです");
}

goto casegoto defaultで明示的に移動する場合も、breakの代わりになります。

C#
switch (value)
{
case 1:
goto case 2;

case 2:
Console.WriteLine("2の処理");
break;
}

重要なのは、各caseの終わりで「次にどこへ制御が移るのか」を明確にすることです。

7-3. caseの値が重複しているときのエラー

同じswitch内で、同じcase値を複数回書くとエラーになります。

C#
int value = 1;

switch (value)
{
case 1:
Console.WriteLine("one");
break;

case 1:
Console.WriteLine("いち");
break;
}

case 1が重複しているため、コンパイルできません。

解決方法は、重複を削除するか、処理をまとめることです。

C#
switch (value)
{
case 1:
Console.WriteLine("one / いち");
break;
}

複数の値で同じ処理をしたい場合は、次のように書きます。

C#
switch (value)
{
case 1:
case 10:
case 100:
Console.WriteLine("対象の値です");
break;
}

パターンマッチングでも、先に書いた条件が後の条件をすべて含んでしまう場合、後続のcaseが到達不能になることがあります。

C#
int score = 95;

switch (score)
{
case >= 60:
Console.WriteLine("合格");
break;

case >= 90:
Console.WriteLine("高得点");
break;
}

95は先に>= 60に一致するため、case >= 90は意味を持ちません。正しくは順番を入れ替えます。

C#
switch (score)
{
case >= 90:
Console.WriteLine("高得点");
break;

case >= 60:
Console.WriteLine("合格");
break;
}

7-4. 到達不能コードになる原因

到達不能コードとは、絶対に実行されないコードのことです。

switchでは、条件の順番やreturnthrowの後にコードを書いた場合に発生しやすいです。

C#
switch (value)
{
case >= 0:
Console.WriteLine("0以上です");
break;

case 10:
Console.WriteLine("10です");
break;
}

case 10case >= 0に含まれるため、到達できません。

解決するには、より具体的な条件を先に書きます。

C#
switch (value)
{
case 10:
Console.WriteLine("10です");
break;

case >= 0:
Console.WriteLine("0以上です");
break;
}

returnの後に処理を書いた場合も到達不能になります。

C#
switch (value)
{
case 1:
return;
Console.WriteLine("ここは実行されません");
}

この場合、returnの後のコードを削除する必要があります。

switchでは、上から順に一致判定されることを意識して、条件の広いものは後ろ、条件の狭いものは前に書くのが基本です。

7-5. switch式で網羅性が不足する場合

switch式では、すべての可能性を網羅していないと警告が出ることがあります。

C#
string message = status switch
{
PaymentStatus.Unpaid => "未払い",
PaymentStatus.Paid => "支払い済み"
};

PaymentStatus.Refundedに対応するアームがないため、網羅性が不足しています。

解決方法は、すべての値を書くことです。

C#
string message = status switch
{
PaymentStatus.Unpaid => "未払い",
PaymentStatus.Paid => "支払い済み",
PaymentStatus.Refunded => "返金済み"
};

または、_を追加します。

C#
string message = status switch
{
PaymentStatus.Unpaid => "未払い",
PaymentStatus.Paid => "支払い済み",
PaymentStatus.Refunded => "返金済み",
_ => "不明"
};

想定外の値を許可しない場合は、例外を投げます。

C#
string message = status switch
{
PaymentStatus.Unpaid => "未払い",
PaymentStatus.Paid => "支払い済み",
PaymentStatus.Refunded => "返金済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, "不明なステータスです")
};

switch式では、どのアームにも一致しなかった場合に実行時例外が発生する可能性があります。そのため、基本的には_を用意しておくと安全です。

7-6. nullを考慮しないことで起きるエラー

switchで文字列やオブジェクトを扱うときは、nullに注意が必要です。

たとえば、次のコードはinputnullの場合に例外が発生します。

C#
string? input = null;

switch (input.ToLower())
{
case "yes":
Console.WriteLine("はい");
break;
case "no":
Console.WriteLine("いいえ");
break;
}

inputnullなのにToLower()を呼んでいるため、NullReferenceExceptionになります。

解決方法の1つは、null条件演算子を使うことです。

C#
switch (input?.ToLower())
{
case "yes":
Console.WriteLine("はい");
break;
case "no":
Console.WriteLine("いいえ");
break;
case null:
Console.WriteLine("入力がありません");
break;
default:
Console.WriteLine("不明な入力です");
break;
}

または、先にnullチェックします。

C#
if (input is null)
{
Console.WriteLine("入力がありません");
return;
}

switch (input.ToLower())
{
case "yes":
Console.WriteLine("はい");
break;
case "no":
Console.WriteLine("いいえ");
break;
}

switch式でもnullを明示できます。

C#
string result = input switch
{
null => "入力がありません",
"yes" => "はい",
"no" => "いいえ",
_ => "不明な入力です"
};

nullがありえる値を扱うときは、case nullまたはnull =>を忘れないようにしましょう。

8. switch文の実践的な使い方

ここでは、実務や学習でよく使うswitchの具体例を紹介します。

8-1. メニュー選択をswitchで処理する

コンソールアプリなどで、ユーザーが入力したメニュー番号に応じて処理を分ける例です。

C#
Console.WriteLine("1: 一覧表示");
Console.WriteLine("2: 追加");
Console.WriteLine("3: 削除");
Console.WriteLine("0: 終了");
Console.Write("番号を入力してください: ");

string? input = Console.ReadLine();

switch (input)
{
case "1":
Console.WriteLine("一覧を表示します");
break;

case "2":
Console.WriteLine("データを追加します");
break;

case "3":
Console.WriteLine("データを削除します");
break;

case "0":
Console.WriteLine("終了します");
break;

default:
Console.WriteLine("正しい番号を入力してください");
break;
}

ユーザー入力は想定外の値が入ることが多いため、defaultを用意しておくことが重要です。

数値に変換してから分岐する場合は、int.TryParseを使うと安全です。

C#
if (!int.TryParse(input, out int menu))
{
Console.WriteLine("数値を入力してください");
return;
}

switch (menu)
{
case 1:
Console.WriteLine("一覧を表示します");
break;
case 2:
Console.WriteLine("追加します");
break;
case 3:
Console.WriteLine("削除します");
break;
case 0:
Console.WriteLine("終了します");
break;
default:
Console.WriteLine("正しい番号を入力してください");
break;
}

8-2. ステータスコードをswitchで判定する

HTTPステータスコードのように、数値の意味が決まっているものはswitchで扱いやすいです。

C#
int statusCode = 404;

switch (statusCode)
{
case 200:
Console.WriteLine("成功です");
break;
case 400:
Console.WriteLine("リクエストが不正です");
break;
case 401:
Console.WriteLine("認証が必要です");
break;
case 403:
Console.WriteLine("アクセス権限がありません");
break;
case 404:
Console.WriteLine("見つかりません");
break;
case 500:
Console.WriteLine("サーバーエラーです");
break;
default:
Console.WriteLine("不明なステータスコードです");
break;
}

範囲で判定したい場合は、関係パターンを使えます。

C#
string category = statusCode switch
{
>= 200 and < 300 => "成功",
>= 300 and < 400 => "リダイレクト",
>= 400 and < 500 => "クライアントエラー",
>= 500 and < 600 => "サーバーエラー",
_ => "不明"
};

ステータスコードのように範囲ごとに意味がある値では、switch式が特に便利です。

8-3. ユーザー入力をswitchで分岐する

ユーザー入力を文字列で受け取り、コマンドごとに処理を分ける例です。

C#
Console.Write("コマンドを入力してください: ");
string? command = Console.ReadLine();

switch (command?.ToLower())
{
case "start":
Console.WriteLine("開始します");
break;

case "stop":
Console.WriteLine("停止します");
break;

case "restart":
Console.WriteLine("再起動します");
break;

case "exit":
Console.WriteLine("終了します");
break;

case null:
Console.WriteLine("入力がありません");
break;

default:
Console.WriteLine("不明なコマンドです");
break;
}

command?.ToLower()としているため、commandnullでも例外になりません。

より丁寧に書くなら、空白も考慮します。

C#
string? command = Console.ReadLine();

switch (command?.Trim().ToLower())
{
case "start":
Console.WriteLine("開始します");
break;
case "stop":
Console.WriteLine("停止します");
break;
case "":
case null:
Console.WriteLine("コマンドを入力してください");
break;
default:
Console.WriteLine("不明なコマンドです");
break;
}

ユーザー入力では、null、空文字、大小文字、余分な空白を考慮するのがポイントです。

8-4. enumとswitchで状態管理する

状態管理では、文字列や数値よりもenumを使うと安全です。

C#
enum TaskStatus
{
Todo,
Doing,
Done,
Canceled
}

switchで状態ごとの処理を書きます。

C#
TaskStatus status = TaskStatus.Doing;

switch (status)
{
case TaskStatus.Todo:
Console.WriteLine("未着手です");
break;

case TaskStatus.Doing:
Console.WriteLine("作業中です");
break;

case TaskStatus.Done:
Console.WriteLine("完了しています");
break;

case TaskStatus.Canceled:
Console.WriteLine("キャンセルされました");
break;

default:
throw new ArgumentOutOfRangeException(nameof(status), status, "不明な状態です");
}

状態に応じて次の状態へ遷移させる処理も書けます。

C#
TaskStatus GetNextStatus(TaskStatus status)
{
return status switch
{
TaskStatus.Todo => TaskStatus.Doing,
TaskStatus.Doing => TaskStatus.Done,
TaskStatus.Done => TaskStatus.Done,
TaskStatus.Canceled => TaskStatus.Canceled,
_ => throw new ArgumentOutOfRangeException(nameof(status), status, "不明な状態です")
};
}

enumswitch式を組み合わせると、状態遷移のルールを簡潔に表現できます。

8-5. switch式で変換処理をシンプルに書く

switch式は、値を別の値に変換する処理に向いています。

C#
enum UserRole
{
Admin,
Editor,
Viewer,
Guest
}

権限名を表示用の文字列に変換します。

C#
string GetRoleName(UserRole role)
{
return role switch
{
UserRole.Admin => "管理者",
UserRole.Editor => "編集者",
UserRole.Viewer => "閲覧者",
UserRole.Guest => "ゲスト",
_ => "不明"
};
}

権限に応じてアクセス可否を返すこともできます。

C#
bool CanEdit(UserRole role)
{
return role switch
{
UserRole.Admin => true,
UserRole.Editor => true,
UserRole.Viewer => false,
UserRole.Guest => false,
_ => false
};
}

orパターンを使うと、さらに短くできます。

C#
bool CanEdit(UserRole role)
{
return role switch
{
UserRole.Admin or UserRole.Editor => true,
_ => false
};
}

単純な変換処理では、switch文よりswitch式のほうが簡潔で読みやすいことが多いです。

9. switch文を書くときの注意点とベストプラクティス

switchは便利な構文ですが、使い方によっては読みにくくなったり、保守しづらくなったりします。

ここでは、実務で意識したいベストプラクティスを紹介します。

9-1. caseが増えすぎたときの見直しポイント

caseが増えすぎると、switch文は読みにくくなります。

C#
switch (type)
{
case "A":
// 処理
break;
case "B":
// 処理
break;
case "C":
// 処理
break;
case "D":
// 処理
break;
case "E":
// 処理
break;
}

caseが多くなった場合は、次の点を見直しましょう。

まず、各caseの処理をメソッドに切り出せるか確認します。

C#
switch (type)
{
case "A":
HandleA();
break;
case "B":
HandleB();
break;
case "C":
HandleC();
break;
}

値から処理や結果を引くだけなら、辞書を使う方法もあります。

C#
var messages = new Dictionary<int, string>
{
{ 200, "OK" },
{ 404, "Not Found" },
{ 500, "Server Error" }
};

string message = messages.TryGetValue(code, out var value)
? value
: "Unknown";

種類ごとに大きく処理が異なる場合は、クラスに分ける設計も検討できます。

switchは便利ですが、巨大なswitchは設計を見直すサインになることがあります。

9-2. defaultで想定外の値を扱う

defaultは、想定外の値を扱う重要な場所です。

C#
switch (status)
{
case TaskStatus.Todo:
Console.WriteLine("未着手");
break;
case TaskStatus.Doing:
Console.WriteLine("作業中");
break;
case TaskStatus.Done:
Console.WriteLine("完了");
break;
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "不明な状態です");
}

defaultで何もせずに無視すると、バグを見逃す可能性があります。

C#
default:
break;

もちろん、何もしないことが仕様であれば問題ありません。ただし、その場合はコメントを入れておくと意図が伝わります。

C#
default:
// その他の状態では処理しない
break;

想定外の値が来てはいけない場合は、例外を投げるのが安全です。

C#
default:
throw new InvalidOperationException("想定外の状態です");

ログを残したい場合は、ログ出力も検討しましょう。

C#
default:
logger.LogWarning("不明なステータスです: {Status}", status);
break;

9-3. マジックナンバーを避ける

switchで数値を直接書くと、意味がわかりにくくなることがあります。

C#
switch (status)
{
case 1:
Console.WriteLine("有効");
break;
case 2:
Console.WriteLine("無効");
break;
}

この12が何を意味するのか、コードだけではわかりません。このような意味のわかりにくい数値をマジックナンバーと呼びます。

改善方法の1つは、定数を使うことです。

C#
const int Active = 1;
const int Inactive = 2;

switch (status)
{
case Active:
Console.WriteLine("有効");
break;
case Inactive:
Console.WriteLine("無効");
break;
}

さらに適しているのは、enumを使う方法です。

C#
enum AccountStatus
{
Active,
Inactive
}

switch (status)
{
case AccountStatus.Active:
Console.WriteLine("有効");
break;
case AccountStatus.Inactive:
Console.WriteLine("無効");
break;
}

enumを使うと、値の意味が明確になり、入力ミスも減らせます。

9-4. enum・定数・辞書との使い分け

switchを書くときは、enum、定数、辞書のどれを使うかも重要です。

値の種類が固定されているなら、enumが向いています。

C#
enum OrderStatus
{
Pending,
Paid,
Shipped,
Canceled
}

意味のある単一の値を使いたいだけなら、定数でも十分です。

C#
const int MaxRetryCount = 3;

値から値へ変換するだけなら、辞書が向いていることもあります。

C#
var labels = new Dictionary<OrderStatus, string>
{
{ OrderStatus.Pending, "確認中" },
{ OrderStatus.Paid, "支払い済み" },
{ OrderStatus.Shipped, "発送済み" },
{ OrderStatus.Canceled, "キャンセル" }
};

一方、値ごとに処理内容が異なる場合は、switchが適しています。

C#
switch (status)
{
case OrderStatus.Pending:
Confirm();
break;
case OrderStatus.Paid:
PrepareShipping();
break;
case OrderStatus.Shipped:
Track();
break;
}

単純な対応表なら辞書、処理の分岐ならswitch、値の種類を表すならenumと考えると使い分けやすいです。

9-5. 可読性を高めるインデントと命名

switch文は、インデントが崩れると一気に読みにくくなります。

読みやすい例です。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("確認中");
break;

case OrderStatus.Paid:
Console.WriteLine("支払い済み");
break;

case OrderStatus.Shipped:
Console.WriteLine("発送済み");
break;

default:
Console.WriteLine("不明");
break;
}

caseごとに空行を入れると、処理のまとまりが見やすくなります。

変数名やenum名も重要です。

悪い例です。

C#
switch (x)
{
case 1:
DoA();
break;
}

何を判定しているのかわかりません。

よい例です。

C#
switch (orderStatus)
{
case OrderStatus.Paid:
PrepareShipping();
break;
}

switchの対象には、意味のある名前を付けましょう。

case内の処理が長い場合は、メソッド名で意図を表すと読みやすくなります。

C#
switch (orderStatus)
{
case OrderStatus.Paid:
PrepareShipping();
break;

case OrderStatus.Canceled:
ProcessRefund();
break;
}

9-6. テストしやすいswitchの書き方

switchを含む処理は、テストしやすい形にしておくことが大切です。

たとえば、コンソール出力を直接行うより、値を返すメソッドにするとテストしやすくなります。

テストしづらい例です。

C#
void PrintStatus(OrderStatus status)
{
switch (status)
{
case OrderStatus.Paid:
Console.WriteLine("支払い済み");
break;
case OrderStatus.Canceled:
Console.WriteLine("キャンセル");
break;
}
}

テストしやすい例です。

C#
string GetStatusLabel(OrderStatus status)
{
return status switch
{
OrderStatus.Paid => "支払い済み",
OrderStatus.Canceled => "キャンセル",
_ => "不明"
};
}

この形なら、戻り値を比較するだけでテストできます。

C#
string label = GetStatusLabel(OrderStatus.Paid);

if (label != "支払い済み")
{
throw new Exception("テスト失敗");
}

また、switch内に外部サービス呼び出しやデータベース処理を直接書きすぎると、テストが難しくなります。

C#
switch (status)
{
case OrderStatus.Paid:
database.Save();
mailService.Send();
break;
}

このような場合は、処理をメソッドやクラスに分けるとテストしやすくなります。

C#
switch (status)
{
case OrderStatus.Paid:
ProcessPaidOrder();
break;
}

switchは分岐の役割に集中させ、具体的な処理は別メソッドに切り出すのが基本です。

10. C# switchに関するよくある質問

ここでは、C#のswitch文やswitch式についてよくある疑問に答えます。

10-1. C#のswitch文で文字列は使える?

はい、C#のswitch文では文字列を使えます。

C#
string command = "start";

switch (command)
{
case "start":
Console.WriteLine("開始します");
break;
case "stop":
Console.WriteLine("停止します");
break;
default:
Console.WriteLine("不明なコマンドです");
break;
}

ただし、大文字・小文字は区別されます。

C#
"Start""start"は別の文字列です。

大文字・小文字を無視したい場合は、ToLower()ToUpper()で変換してから比較する方法があります。

C#
switch (command?.ToLower())
{
case "start":
Console.WriteLine("開始します");
break;
}

nullの可能性がある場合は、?.を使うか、先にnullチェックしましょう。

10-2. C#のswitch文で複数条件は書ける?

はい、書けます。

複数の値で同じ処理をしたい場合は、caseを並べます。

C#
switch (month)
{
case 12:
case 1:
case 2:
Console.WriteLine("冬です");
break;
}

C#のパターンマッチングを使えば、orで複数条件を書けます。

C#
string type = day switch
{
DayOfWeek.Saturday or DayOfWeek.Sunday => "休日",
_ => "平日"
};

追加条件を付けたい場合は、when句を使います。

C#
switch (score)
{
case >= 90 when isAttendanceGood:
Console.WriteLine("評価A");
break;
}

複数の値を同時に判定したい場合は、タプルパターンも使えます。

C#
string result = (isMember, totalPrice) switch
{
(true, >= 10000) => "会員特別割引",
(true, _) => "会員割引",
_ => "通常"
};

10-3. C#のswitch文で範囲指定はできる?

はい、関係パターンを使うと範囲指定できます。

C#
int score = 85;

switch (score)
{
case >= 90:
Console.WriteLine("A");
break;
case >= 80:
Console.WriteLine("B");
break;
case >= 70:
Console.WriteLine("C");
break;
default:
Console.WriteLine("D");
break;
}

範囲を明確に書くなら、andを使います。

C#
switch (score)
{
case >= 80 and <= 100:
Console.WriteLine("高得点");
break;
case >= 60 and < 80:
Console.WriteLine("合格");
break;
case >= 0 and < 60:
Console.WriteLine("不合格");
break;
default:
Console.WriteLine("不正な点数です");
break;
}

範囲条件では、順番が重要です。広い条件を先に書くと、狭い条件に到達しないことがあります。

10-4. switch文とswitch式はどちらを使うべき?

処理を実行したい場合はswitch文、値を返したい場合はswitch式を使うのが基本です。

switch文が向いている例です。

C#
switch (command)
{
case "save":
Save();
break;
case "load":
Load();
break;
}

switch式が向いている例です。

C#
string label = status switch
{
OrderStatus.Pending => "確認中",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
_ => "不明"
};

単純な変換ならswitch式が読みやすいです。

複数行の処理、ログ出力、外部サービス呼び出しなどを含む場合は、switch文のほうが適しています。

10-5. switch文でbreakを書かない方法はある?

あります。

returnでメソッドを終了する場合は、breakは不要です。

C#
string GetMessage(int value)
{
switch (value)
{
case 1:
return "one";
case 2:
return "two";
default:
return "other";
}
}

throwで例外を投げる場合も不要です。

C#
switch (value)
{
case 1:
Console.WriteLine("1です");
break;
default:
throw new ArgumentException("不正な値です");
}

goto casegoto defaultを使う方法もあります。

C#
switch (value)
{
case 1:
goto case 2;
case 2:
Console.WriteLine("2の処理");
break;
}

また、値を返すだけならswitch式を使うと、そもそもbreakが不要になります。

C#
string message = value switch
{
1 => "one",
2 => "two",
_ => "other"
};

10-6. if文とswitch文はどちらが速い?

単純なケースでは、if文とswitch文の速度差を気にする必要はほとんどありません。

多くの場合、可読性を優先して選ぶべきです。

同じ変数を複数の値と比較するなら、switchのほうが読みやすいです。

C#
switch (command)
{
case "start":
Start();
break;
case "stop":
Stop();
break;
}

複雑な条件式を組み合わせるなら、ifのほうが自然です。

C#
if (user.IsActive && user.Age >= 20 && user.HasPermission)
{
Allow();
}

パフォーマンスが本当に重要な場面では、実際のデータと環境で計測することが大切です。通常のアプリケーション開発では、「速そうだから」という理由だけでswitchを選ぶより、読みやすく保守しやすいコードを書くほうが重要です。

まとめ

C#のswitch文は、1つの値や式に対して複数の条件分岐を行うための便利な構文です。

基本的な構文では、switchcasebreakdefaultを使います。

C#
switch (value)
{
case 1:
Console.WriteLine("1です");
break;
default:
Console.WriteLine("その他です");
break;
}

数値、文字列、enumなどの分岐に使えるだけでなく、現在のC#ではパターンマッチングによって、型判定、範囲条件、プロパティ判定、タプルによる複数値判定なども書けます。

C#
string result = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
_ => "D"
};

switch文は処理を分岐したいとき、switch式は値を返したいときに使うとよいでしょう。

C#
string message = status switch
{
OrderStatus.Pending => "確認中",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
_ => "不明"
};

また、breakの書き忘れ、caseの重複、到達不能コード、switch式の網羅性不足、nullの考慮漏れは、よくあるエラーです。特にdefault_を適切に使い、想定外の値に備えることが大切です。

switchは、うまく使えばコードを短く、読みやすく、保守しやすくできます。一方で、caseが増えすぎたり、複雑な処理を詰め込みすぎたりすると逆効果になります。

単純な値の分岐にはswitch、複雑な条件にはif、値の変換にはswitch式、大量の対応表には辞書、状態や種類の表現にはenumを使うなど、目的に応じて使い分けることが大切です。