C#のswitchでenumを使う方法|基本構文・switch式・default処理まで実例で解説

はじめに

C#では、switchenumを組み合わせることで、状態・種別・権限・ステータスなどに応じた処理を読みやすく整理できます。

たとえば、注文ステータスが「未処理」「発送済み」「キャンセル済み」のどれかによって処理を変えたい場合、数値や文字列で分岐するよりも、enumで意味のある名前を付けてswitchする方が安全で保守しやすくなります。

この記事では、C#のswitchenumを使う基本構文から、switch式、default処理、例外処理、実践的な書き方までをサンプルコード付きで解説します。

1. C#のswitchでenumを使う基本

1-1. enumとswitchを組み合わせると何ができるか

enumは、関連する定数に名前を付けてまとめるための型です。switchは、値に応じて処理を分岐する構文です。

この2つを組み合わせると、次のような分岐をわかりやすく書けます。

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

OrderStatus status = OrderStatus.Paid;

switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

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

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

OrderStatus.Paidのように意味のある名前で分岐できるため、単なる12で条件分岐するよりも、コードの意図が明確になります。

1-2. enumを使った条件分岐が向いているケース

enumswitchの組み合わせは、取り得る値があらかじめ決まっている場合に向いています。

たとえば、次のようなケースです。

C#
enum UserRole
{
Guest,
Member,
Admin
}
C#
enum PaymentMethod
{
CreditCard,
BankTransfer,
CashOnDelivery
}
C#
enum ErrorType
{
Network,
Validation,
Authentication,
Unknown
}

ユーザー権限、注文ステータス、支払い方法、エラー種別、画面モードなど、候補が限定されている値はenumにすると扱いやすくなります。

1-3. if文ではなくswitchを使うメリット

if文でもenumの分岐は書けます。

C#
if (status == OrderStatus.Pending)
{
Console.WriteLine("支払い待ちです");
}
else if (status == OrderStatus.Paid)
{
Console.WriteLine("支払い済みです");
}
else if (status == OrderStatus.Shipped)
{
Console.WriteLine("発送済みです");
}

ただし、分岐が増えると条件式が縦に長くなり、読みづらくなります。

switchを使うと、ひとつの値に対する分岐であることが明確になります。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

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

enumのように「ひとつの値がどれに該当するか」を判定する処理では、if文よりもswitchの方が見通しよく書けることが多いです。

1-4. この記事で扱うサンプルenum

この記事では、主に次のenumを使って解説します。

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

また、実践例ではユーザー権限やエラー種別のenumも使います。

C#
enum UserRole
{
Guest,
Member,
Admin
}
C#
enum ErrorType
{
Network,
Validation,
Authentication,
Unknown
}

2. C#のenumの基本構文

2-1. enumの定義方法

C#でenumを定義する基本構文は次のとおりです。

C#
enum EnumName
{
Member1,
Member2,
Member3
}

たとえば、注文ステータスを表すenumは次のように定義できます。

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

PendingPaidのような各項目を、enumメンバーと呼びます。

2-2. enumメンバーの値と既定値

C#のenumメンバーには、内部的に数値が割り当てられます。値を明示しない場合、最初のメンバーは0、次は1、その次は2というように増えていきます。列挙型の既定値は(E)0であり、0に対応するメンバーが定義されていない場合でも(E)0になります。Microsoft Learn

C#
enum OrderStatus
{
Pending, // 0
Paid, // 1
Shipped, // 2
Canceled // 3
}

値を明示することもできます。

C#
enum OrderStatus
{
Pending = 10,
Paid = 20,
Shipped = 30,
Canceled = 40
}

ただし、特別な理由がなければ、無理に数値を指定しなくても問題ありません。

2-3. enum型の変数を宣言する方法

enum型の変数は、通常の型と同じように宣言できます。

C#
OrderStatus status = OrderStatus.Pending;

メソッドの引数や戻り値にも使えます。

C#
void UpdateOrderStatus(OrderStatus status)
{
Console.WriteLine(status);
}
C#
OrderStatus GetInitialStatus()
{
return OrderStatus.Pending;
}

enumを使うと、引数に渡せる値の意味が明確になります。

2-4. enumと数値の関係

enumは内部的には整数値を持ちます。C#の列挙型の基になる型は、明示しない場合はintです。必要に応じてbytelongなどを指定できますが、多くのケースでは既定のintで十分です。Microsoft Learn

C#
enum OrderStatus : int
{
Pending = 0,
Paid = 1,
Shipped = 2,
Canceled = 3
}

数値へキャストすることもできます。

C#
OrderStatus status = OrderStatus.Shipped;

int value = (int)status;

Console.WriteLine(value); // 2

逆に、数値からenumへキャストすることもできます。

C#
OrderStatus status = (OrderStatus)2;

Console.WriteLine(status); // Shipped

ただし、enumに定義されていない数値もキャストできる点には注意が必要です。

C#
OrderStatus status = (OrderStatus)999;

Console.WriteLine(status); // 999

このような値がswitchに渡される可能性があるため、default処理や例外処理を考えておくことが大切です。

2-5. enumを使うとコードが読みやすくなる理由

数値や文字列で状態を表すと、コードを読んだときに意味がわかりにくくなります。

C#
int status = 2;

if (status == 2)
{
Console.WriteLine("発送済み");
}

このコードだけでは、2が何を意味するのかすぐにはわかりません。

enumを使うと、値の意味を名前で表現できます。

C#
OrderStatus status = OrderStatus.Shipped;

if (status == OrderStatus.Shipped)
{
Console.WriteLine("発送済み");
}

OrderStatus.Shippedと書かれていれば、「注文ステータスが発送済みの場合」という意図が明確になります。

3. switch文でenumを分岐する方法

3-1. switch文の基本構文

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

C#
switch ()
{
case 条件1:
処理1;
break;

case 条件2:
処理2;
break;

default:
どれにも一致しない場合の処理;
break;
}

switch文は、指定した値に一致するcaseの処理を実行します。C#のswitch文は、式に対するパターンマッチによって実行する処理を選択する構文です。Microsoft Learn

3-2. caseにenumメンバーを指定する書き方

enumswitchで分岐する場合、caseにはenumメンバーを指定します。

C#
OrderStatus status = OrderStatus.Paid;

switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

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

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

case Paid:のようにメンバー名だけを書くのではなく、通常はOrderStatus.Paidのように型名も含めて書きます。

3-3. breakを書き忘れないための注意点

C#のswitch文では、各caseの処理の終わりにbreakreturnthrowなどを書いて、処理の流れを明確に終わらせます。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

breakを書き忘れると、意図しない処理の流れになるため、コンパイルエラーになるケースがあります。

メソッドからすぐに値を返す場合は、breakの代わりにreturnを使えます。

C#
string GetMessage(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
return "支払い待ちです";

case OrderStatus.Paid:
return "支払い済みです";

case OrderStatus.Shipped:
return "発送済みです";

case OrderStatus.Canceled:
return "キャンセル済みです";

default:
return "不明なステータスです";
}
}

3-4. 複数のenum値で同じ処理を実行する方法

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

C#
switch (status)
{
case OrderStatus.Pending:
case OrderStatus.Paid:
Console.WriteLine("まだ発送前です");
break;

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

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

この例では、PendingPaidのどちらでも「まだ発送前です」と表示されます。

C#では、処理を書かずにcaseを並べることで、複数の条件を同じ処理にまとめられます。

3-5. メソッド内でenumに応じた処理を切り替える実例

実際のコードでは、enumをメソッドの引数として受け取り、処理を切り替えることがよくあります。

C#
void ProcessOrder(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い案内メールを送信します");
break;

case OrderStatus.Paid:
Console.WriteLine("出荷準備を開始します");
break;

case OrderStatus.Shipped:
Console.WriteLine("配送状況を確認します");
break;

case OrderStatus.Canceled:
Console.WriteLine("キャンセル処理を行います");
break;

default:
throw new ArgumentOutOfRangeException(
nameof(status),
status,
"未対応の注文ステータスです");
}
}

defaultで例外を投げておくと、想定外の値が渡されたときに問題を早く発見できます。

4. switch式でenumを扱う方法

4-1. switch式の基本構文

C#には、switch文だけでなくswitch式もあります。

switch式は、分岐した結果として値を返すときに便利です。

C#
string message = status switch
{
OrderStatus.Pending => "支払い待ちです",
OrderStatus.Paid => "支払い済みです",
OrderStatus.Shipped => "発送済みです",
OrderStatus.Canceled => "キャンセル済みです",
_ => "不明なステータスです"
};

switch式では、casebreakを書かず、=>を使って「この値ならこの結果を返す」と表現します。

4-2. switch文とswitch式の違い

switch文は、処理を実行するための構文です。

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

一方、switch式は、値を返すための構文です。

C#
string message = status switch
{
OrderStatus.Paid => "支払い済みです",
_ => "その他です"
};

画面表示用の文字列を返す、数値を返す、設定値を返すといった処理では、switch式を使うとコードが短くなります。

4-3. enumを文字列に変換して返す実例

enumの値に応じて表示用の文字列を返す例です。

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

呼び出し側は次のように使えます。

C#
OrderStatus status = OrderStatus.Shipped;

string text = GetStatusText(status);

Console.WriteLine(text); // 発送済み

switch式を使うと、「enumを表示文字列に変換する処理」をシンプルにまとめられます。

4-4. switch式で戻り値をシンプルに書く方法

switch文で戻り値を返す場合、次のように書けます。

C#
string GetStatusText(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
return "支払い待ち";

case OrderStatus.Paid:
return "支払い済み";

case OrderStatus.Shipped:
return "発送済み";

case OrderStatus.Canceled:
return "キャンセル済み";

default:
return "不明";
}
}

同じ処理をswitch式で書くと、より簡潔になります。

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

戻り値を返すだけの分岐であれば、switch式の方が読みやすくなることが多いです。

4-5. アンダースコアでdefault相当の処理を書く方法

switch式では、defaultの代わりに_を使います。

C#
string text = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => "不明"
};

_は、どのパターンにも一致しなかった値を受けるための書き方です。C#のswitch式では、定義済みのenum値に対応するパターンと、未定義の値を扱うための破棄パターンを組み合わせて網羅的に書けます。Microsoft Learn

4-6. switch式を使うときの注意点

switch式では、各分岐の戻り値の型をそろえる必要があります。

次のコードは、文字列と数値が混ざっているため避けるべきです。

C#
var result = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => 1,
_ => "不明"
};

基本的には、すべての分岐で同じ型の値を返すようにします。

C#
string result = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
_ => "不明"
};

また、_を書かない場合、想定外の値が渡されたときに実行時例外が発生する可能性があります。非網羅のswitch式が入力に一致しない場合、SwitchExpressionExceptionが発生します。Microsoft Learn

5. enumのdefault処理と例外処理

5-1. switch文のdefaultの役割

switch文のdefaultは、どのcaseにも一致しなかった場合に実行される処理です。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

default:
Console.WriteLine("不明なステータスです");
break;
}

enumは数値からキャストできるため、定義されていない値が渡される可能性があります。

C#
OrderStatus status = (OrderStatus)999;

このような値に備えるために、defaultを用意しておくと安全です。

5-2. switch式の_パターンの役割

switch式では、defaultの代わりに_を使います。

C#
string message = status switch
{
OrderStatus.Pending => "支払い待ちです",
OrderStatus.Paid => "支払い済みです",
OrderStatus.Shipped => "発送済みです",
OrderStatus.Canceled => "キャンセル済みです",
_ => "不明なステータスです"
};

_は「それ以外すべて」を表します。

switch文のdefaultと同じように、想定外の値を受け止める役割があります。

5-3. 想定外のenum値が渡されたときの対応

想定外のenum値が渡された場合、主に次のような対応が考えられます。

C#
// 1. 不明として扱う
_ => "不明"
C#
// 2. 例外を投げる
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
C#
// 3. ログに出して既定の処理を行う
default:
Console.WriteLine($"未対応のステータスです: {status}");
break;

業務ロジックでは、不明な値をそのまま処理すると不具合につながる可能性があります。

重要な処理では、defaultで握りつぶすのではなく、例外やログで検知できるようにしておくと安心です。

5-4. defaultで例外を投げる実装例

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

C#
void ProcessOrder(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い案内を送信します");
break;

case OrderStatus.Paid:
Console.WriteLine("出荷準備を開始します");
break;

case OrderStatus.Shipped:
Console.WriteLine("配送状況を確認します");
break;

case OrderStatus.Canceled:
Console.WriteLine("キャンセル処理を行います");
break;

default:
throw new ArgumentOutOfRangeException(
nameof(status),
status,
"未対応の注文ステータスです");
}
}

switch式でも例外を投げられます。

C#
string GetStatusText(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => throw new ArgumentOutOfRangeException(
nameof(status),
status,
"未対応の注文ステータスです")
};
}

不正な値を早く発見したい場合は、このように明示的に例外を投げる実装が有効です。

5-5. defaultを省略してよいケースと危険なケース

defaultを省略してよいケースは、すべてのenum値を明示的に処理し、かつ未定義の値が入らない前提を強く保てる場合です。

ただし、次のようなケースではdefault_を用意した方が安全です。

C#
OrderStatus status = (OrderStatus)999;

外部入力、データベース、API、ファイル、ユーザー入力などから値を復元する場合、定義外の値が入る可能性があります。

そのような場面では、defaultで例外を投げる、または不明な値として扱う処理を用意しておきましょう。

6. enumの値を網羅する書き方

6-1. すべてのenumメンバーをcaseに書く重要性

enumswitchで扱うときは、基本的にすべてのメンバーをcaseに書くことが重要です。

C#
switch (status)
{
case OrderStatus.Pending:
break;

case OrderStatus.Paid:
break;

case OrderStatus.Shipped:
break;

case OrderStatus.Canceled:
break;
}

すべての値を明示しておくと、「どのステータスで何をするか」がコード上で確認しやすくなります。

後から見た人も、処理漏れに気づきやすくなります。

6-2. enumに値を追加したときに起きやすい不具合

たとえば、後からReturnedというステータスを追加したとします。

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

このとき、既存のswitchReturnedの処理を追加し忘れると、不具合の原因になります。

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

_ => "不明"があると、Returnedも「不明」として処理されるため、追加漏れに気づきにくくなることがあります。

6-3. switch式の網羅性チェックを活用する方法

switch式では、すべての入力パターンを扱っていない場合にコンパイラが警告を出すことがあります。C#の仕様では、switch式が入力型に対して網羅的でない場合、コンパイラは警告を生成します。Microsoft Learn

たとえば、_を書かずにすべてのenum値を列挙すると、後からenum値を追加したときに警告で気づける場合があります。

C#
string GetStatusText(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み"
};
}

ただし、enumは定義外の数値も持てるため、業務上の安全性を考えるなら_で例外を投げる書き方もよく使われます。

C#
string GetStatusText(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
};
}

6-4. defaultを入れるべきか入れないべきか

defaultを入れるべきかどうかは、目的によって変わります。

外部入力やDBの値を扱う場合は、想定外の値に備えてdefaultを入れる方が安全です。

C#
default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);

一方で、enumに新しい値を追加したときに処理漏れを検出したい場合、安易にdefaultで吸収すると気づきにくくなります。

そのため、重要な処理では次のように考えるとよいでしょう。

C#
// 想定外の値を無視したくない場合
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)

「不明」として表示しても問題ない画面表示では_ => "不明"、業務処理では例外を投げる、といった使い分けが現実的です。

6-5. 保守しやすいenum分岐の書き方

保守しやすいenum分岐にするには、次の点を意識します。

C#
string GetStatusText(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
};
}

まず、すべてのenumメンバーを明示します。

次に、想定外の値をどう扱うかを_defaultで明確にします。

さらに、分岐の中に複雑な処理を詰め込みすぎないことも大切です。複雑な処理は別メソッドに切り出すと、switch自体が読みやすくなります。

7. 実践例:enumとswitchで状態・種別ごとの処理を分ける

7-1. ユーザー権限をenumで分岐する例

ユーザー権限をenumで表す例です。

C#
enum UserRole
{
Guest,
Member,
Admin
}

switch文で権限ごとの処理を分けます。

C#
void ShowMenu(UserRole role)
{
switch (role)
{
case UserRole.Guest:
Console.WriteLine("閲覧メニューを表示します");
break;

case UserRole.Member:
Console.WriteLine("会員メニューを表示します");
break;

case UserRole.Admin:
Console.WriteLine("管理者メニューを表示します");
break;

default:
throw new ArgumentOutOfRangeException(nameof(role), role, null);
}
}

権限の種類が増えた場合も、switchを見ればどの権限にどの処理が対応しているか確認しやすくなります。

7-2. 注文ステータスをenumで分岐する例

注文ステータスによって、次に実行する処理を変える例です。

C#
void ExecuteNextAction(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い依頼を送信します");
break;

case OrderStatus.Paid:
Console.WriteLine("出荷指示を作成します");
break;

case OrderStatus.Shipped:
Console.WriteLine("配送完了を待ちます");
break;

case OrderStatus.Canceled:
Console.WriteLine("在庫を戻します");
break;

default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);
}
}

業務ロジックでは、ステータスごとの処理を明確に分けることで、仕様変更にも対応しやすくなります。

7-3. エラー種別をenumで分岐する例

エラー種別に応じて、表示メッセージやログの出し方を変える例です。

C#
enum ErrorType
{
Network,
Validation,
Authentication,
Unknown
}
C#
string GetErrorMessage(ErrorType errorType)
{
return errorType switch
{
ErrorType.Network => "ネットワークエラーが発生しました",
ErrorType.Validation => "入力内容を確認してください",
ErrorType.Authentication => "ログインが必要です",
ErrorType.Unknown => "不明なエラーが発生しました",
_ => "予期しないエラーが発生しました"
};
}

表示用の文字列を返すだけなら、switch式が向いています。

7-4. 画面表示用の文字列をswitch式で返す例

画面に表示するラベルをswitch式で返す例です。

C#
string GetRoleLabel(UserRole role)
{
return role switch
{
UserRole.Guest => "ゲスト",
UserRole.Member => "会員",
UserRole.Admin => "管理者",
_ => "不明"
};
}

呼び出し側では、次のように使えます。

C#
UserRole role = UserRole.Admin;

Console.WriteLine(GetRoleLabel(role)); // 管理者

enumの名前は英語のままにして、画面表示だけ日本語に変換したい場合にも便利です。

7-5. 業務ロジックでenum分岐を使うときの設計ポイント

業務ロジックでenumswitchを使うときは、分岐の責務を明確にしましょう。

たとえば、画面表示用の変換と、実際の業務処理を同じswitchに混ぜると、コードが複雑になります。

C#
string GetStatusText(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
};
}
C#
void ProcessOrder(OrderStatus status)
{
switch (status)
{
case OrderStatus.Pending:
SendPaymentRequest();
break;

case OrderStatus.Paid:
CreateShipment();
break;

case OrderStatus.Shipped:
TrackDelivery();
break;

case OrderStatus.Canceled:
RestoreStock();
break;

default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);
}
}

表示用、処理用、判定用を分けると、それぞれの目的がはっきりします。

8. enumとswitchでよくあるエラー・つまずき

8-1. caseにenum型ではない値を書いてしまう

switch対象がOrderStatus型なのに、caseに関係のない値を書くとエラーになります。

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

enumを使う場合は、次のようにenumメンバーを指定します。

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

数値ではなく、意味のある名前で書くのがenumを使うメリットです。

8-2. break忘れによるコンパイルエラー

switch文で処理を書いた後、breakreturnを書き忘れるとエラーになることがあります。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");

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

正しくは次のように書きます。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

caseの終わりには、処理の出口を明確に書きましょう。

8-3. switch式で戻り値の型がそろわない

switch式では、戻り値の型がそろっていないと扱いづらくなります。

C#
var result = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => 1,
_ => "不明"
};

このような書き方は避け、同じ型を返すようにします。

C#
string result = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
_ => "不明"
};

文字列を返すならすべて文字列、数値を返すならすべて数値にそろえるのが基本です。

8-4. enumに存在しない数値が渡される

enumは数値からキャストできるため、定義されていない値が入ることがあります。C#の仕様では、列挙型の基になる型の任意の値をその列挙型へキャストできます。Microsoft Learn

C#
OrderStatus status = (OrderStatus)999;

この値は、OrderStatus.PendingでもOrderStatus.Paidでもありません。

そのため、switchではdefault_で対応しておくと安全です。

C#
string text = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
};

外部から受け取った数値をenumに変換する場合は、Enum.IsDefinedなどで定義済みの値か確認することも検討しましょう。

C#
int value = 999;

if (Enum.IsDefined(typeof(OrderStatus), value))
{
OrderStatus status = (OrderStatus)value;
Console.WriteLine(status);
}
else
{
Console.WriteLine("未定義のステータスです");
}

8-5. null許容enumをswitchで扱う場合の注意点

enumは値型なので、通常はnullになりません。

ただし、OrderStatus?のように?を付けると、null許容enumになります。

C#
OrderStatus? status = null;

switch式で扱う場合は、nullの分岐も書けます。

C#
string text = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
null => "ステータス未設定",
_ => "不明"
};

nullがあり得る設計なら、通常のenum値だけでなく、nullの場合にどう扱うかも明確にしておきましょう。

9. enumとswitchを使うときのベストプラクティス

9-1. enum名とメンバー名をわかりやすくする

enumは、名前によって意味を伝えるための仕組みです。

そのため、名前が曖昧だとメリットが薄れます。

C#
enum Status
{
A,
B,
C
}

このような名前では、何を表しているのか分かりません。

次のように、意味が伝わる名前にしましょう。

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

enum名は何の種類を表すのか、メンバー名はそれぞれの状態や種別を表すようにします。

9-2. 分岐が増えすぎたときは設計を見直す

enumswitchは便利ですが、分岐が増えすぎるとコードが読みにくくなります。

C#
switch (status)
{
case OrderStatus.Pending:
// 多くの処理
break;

case OrderStatus.Paid:
// 多くの処理
break;

case OrderStatus.Shipped:
// 多くの処理
break;

case OrderStatus.Canceled:
// 多くの処理
break;
}

caseの中が長くなる場合は、処理をメソッドに分けましょう。

C#
switch (status)
{
case OrderStatus.Pending:
HandlePending();
break;

case OrderStatus.Paid:
HandlePaid();
break;

case OrderStatus.Shipped:
HandleShipped();
break;

case OrderStatus.Canceled:
HandleCanceled();
break;

default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);
}

さらに分岐が複雑になりすぎる場合は、クラス設計やポリモーフィズムの利用も検討します。

9-3. 戻り値がある処理はswitch式を検討する

値を返すだけの分岐では、switch式が便利です。

C#
string label = role switch
{
UserRole.Guest => "ゲスト",
UserRole.Member => "会員",
UserRole.Admin => "管理者",
_ => "不明"
};

switch文で書くよりも短く、代入やreturnと相性が良いです。

C#
string GetRoleLabel(UserRole role)
{
return role switch
{
UserRole.Guest => "ゲスト",
UserRole.Member => "会員",
UserRole.Admin => "管理者",
_ => throw new ArgumentOutOfRangeException(nameof(role), role, null)
};
}

表示名、メッセージ、設定値などを返す処理では、まずswitch式を検討するとよいでしょう。

9-4. defaultでは握りつぶさず例外やログを検討する

default_で何もせずに処理を終えると、不具合に気づきにくくなります。

C#
default:
break;

このような書き方は、意図が明確でない限り避けた方が無難です。

想定外の値を検知したい場合は、例外を投げます。

C#
default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);

運用上、処理を止めたくない場合でも、ログを残すなどして原因を追えるようにしましょう。

C#
default:
Console.WriteLine($"未対応のステータスです: {status}");
break;

9-5. テストでenumの全パターンを確認する

enumを使った分岐は、すべてのパターンをテストすることが重要です。

C#
Console.WriteLine(GetStatusText(OrderStatus.Pending));
Console.WriteLine(GetStatusText(OrderStatus.Paid));
Console.WriteLine(GetStatusText(OrderStatus.Shipped));
Console.WriteLine(GetStatusText(OrderStatus.Canceled));

特に、enumに新しい値を追加したときは、既存のswitchが対応できているか確認しましょう。

未定義の値も重要なテスト対象です。

C#
OrderStatus invalidStatus = (OrderStatus)999;

try
{
Console.WriteLine(GetStatusText(invalidStatus));
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("想定外の値を検知しました");
}

業務上重要な分岐では、正常系だけでなく異常系もテストしておくと安心です。

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

10-1. enumをswitchで使うときcaseには何を書く?

caseには、enumメンバーを書きます。

C#
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("支払い待ちです");
break;

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

基本的には、OrderStatus.Pendingのように「enum型名.メンバー名」の形で書きます。

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

処理を実行するならswitch文、値を返すだけならswitch式が向いています。

C#
switch (status)
{
case OrderStatus.Paid:
Console.WriteLine("支払い済みです");
break;
}
C#
string text = status switch
{
OrderStatus.Paid => "支払い済み",
_ => "その他"
};

表示文字列を返す処理や、メソッドの戻り値を返す処理ではswitch式が便利です。

10-3. enumのdefault値は何になる?

enumの既定値は0です。

正確には、列挙型Eの既定値は(E)0です。0に対応するメンバーがなくても(E)0になります。Microsoft Learn

C#
enum Priority
{
Low = 1,
Middle = 2,
High = 3
}

Priority priority = default;

Console.WriteLine(priority); // 0

そのため、必要に応じて0に対応するメンバーを定義しておくと扱いやすくなります。

C#
enum Priority
{
None = 0,
Low = 1,
Middle = 2,
High = 3
}

10-4. defaultは必ず書く必要がある?

必ず書かなければならないわけではありません。

ただし、enumには定義外の数値が入る可能性があるため、実務ではdefault_を用意しておくことが多いです。

C#
default:
throw new ArgumentOutOfRangeException(nameof(status), status, null);

画面表示では「不明」として扱い、業務処理では例外を投げるなど、用途に応じて使い分けましょう。

10-5. enumに数値をキャストした値もswitchで処理できる?

処理できます。

C#
OrderStatus status = (OrderStatus)2;

switch (status)
{
case OrderStatus.Shipped:
Console.WriteLine("発送済みです");
break;
}

ただし、定義されていない数値もキャストできます。

C#
OrderStatus status = (OrderStatus)999;

この場合、どのcaseにも一致しないため、defaultで対応する必要があります。

C#
switch (status)
{
case OrderStatus.Pending:
case OrderStatus.Paid:
case OrderStatus.Shipped:
case OrderStatus.Canceled:
Console.WriteLine("定義済みのステータスです");
break;

default:
Console.WriteLine("未定義のステータスです");
break;
}

10-6. Flags属性付きenumはswitchで扱える?

Flags属性付きのenumswitchで扱えます。

C#
[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Execute = 4
}

ただし、Flags付きenumは複数の値を組み合わせられます。

C#
Permission permission = Permission.Read | Permission.Write;

完全一致で分岐するならswitchでも書けます。

C#
switch (permission)
{
case Permission.Read:
Console.WriteLine("読み取りのみ");
break;

case Permission.Write:
Console.WriteLine("書き込みのみ");
break;

case Permission.Read | Permission.Write:
Console.WriteLine("読み取りと書き込み");
break;
}

ただし、組み合わせが多い場合はswitchよりもビット演算やHasFlagを使う方が自然です。

C#
if (permission.HasFlag(Permission.Read))
{
Console.WriteLine("読み取り権限があります");
}

if (permission.HasFlag(Permission.Write))
{
Console.WriteLine("書き込み権限があります");
}

Flags付きenumでは、単一の値を分岐するのか、権限の有無を判定するのかを意識して使い分けましょう。

まとめ

C#では、switchenumを組み合わせることで、状態や種別に応じた条件分岐を読みやすく書けます。

enumを使うと、数値や文字列ではなく意味のある名前で値を扱えるため、コードの意図が明確になります。

switch文は処理を分岐したい場合に向いており、switch式は値を返す分岐に向いています。

C#
string text = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
OrderStatus.Shipped => "発送済み",
OrderStatus.Canceled => "キャンセル済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
};

また、enumには定義外の数値が入る可能性があるため、default_で想定外の値をどう扱うかを明確にしておくことが重要です。

画面表示のように「不明」として扱ってよい処理もあれば、業務ロジックのように例外で検知した方がよい処理もあります。

enumのすべての値を意識してswitchを書くことで、C#のコードはより安全で保守しやすくなります。