C# switch whenの使い方を徹底解説|if文との違い・条件分岐・パターンマッチングの実例付き

はじめに

C#で条件分岐を書くとき、もっともよく使われる構文はif文とswitch文です。その中でも、switchに追加条件を付けられるwhenは、複雑になりがちな分岐処理を読みやすく整理できる便利な機能です。

たとえば、単純に「値が1ならA、2ならB」と分けるだけなら通常のswitchで十分です。しかし実務では、「値が同じでも、別の条件によって処理を変えたい」「型によって分岐しつつ、さらにプロパティの値も見たい」「ステータスが同じでも、ユーザー権限によって結果を変えたい」といった場面がよくあります。

このようなときに役立つのが、caseに条件を追加できるwhenです。C#のswitch文は、パターンに一致し、さらにガード条件がある場合はその条件がtrueになった最初のcaseを実行します。また、caseは上から順に評価されるため、条件の並び順も重要です。Microsoft Learn

この記事では、C#のswitch whenの基本構文から、if文との違い、パターンマッチング、switch式での使い方、実務で使える設計例まで、具体的なコード付きで解説します。

1. C#のswitch whenとは?検索ユーザーがまず知りたい基本

1-1. switch文で使うwhen句の役割

C#のswitch whenとは、switch文やswitch式のcaseに追加条件を付けるための構文です。

通常のswitch文では、指定した値やパターンに一致した場合に処理を実行します。そこにwhenを付けると、「値やパターンが一致し、かつ追加条件も満たす場合だけ実行する」という書き方ができます。

C#
int score = 85;

switch (score)
{
case int n when n >= 80:
Console.WriteLine("高得点です");
break;

case int n when n >= 60:
Console.WriteLine("合格です");
break;

default:
Console.WriteLine("不合格です");
break;
}

この例では、scoreの値をint nとして受け取り、そのうえでwhen n >= 80when n >= 60という条件を判定しています。

whenは、単なる値の一致だけでは表現しにくい「範囲」「複数条件」「型やプロパティに応じた条件分岐」を書くときに便利です。

1-2. whenは「caseに追加条件を付ける」ための構文

whenは、caseの後ろに書く追加条件です。

基本的な考え方は次のとおりです。

C#
case パターン when 条件式:
// 条件を満たしたときの処理
break;

たとえば次のように書くと、「値がAdminで、さらにisActivetrueのときだけ」処理を実行できます。

C#
string role = "Admin";
bool isActive = true;

switch (role)
{
case "Admin" when isActive:
Console.WriteLine("有効な管理者です");
break;

case "Admin":
Console.WriteLine("無効な管理者です");
break;

default:
Console.WriteLine("一般ユーザーです");
break;
}

このように、whencaseの条件をさらに細かく絞り込むために使います。

1-3. switch whenが活躍する場面

switch whenが活躍する代表的な場面は、次のようなケースです。

C#
switch (user.Role)
{
case "Admin" when user.IsActive:
Console.WriteLine("管理者ページを表示");
break;

case "Admin" when !user.IsActive:
Console.WriteLine("アカウントが無効です");
break;

case "Member" when user.Point >= 1000:
Console.WriteLine("プレミアム会員ページを表示");
break;

default:
Console.WriteLine("通常ページを表示");
break;
}

この例では、Roleだけでなく、IsActivePointといった追加条件も見ています。

実務では、次のような処理でよく使われます。

入力値のバリデーション、ユーザー権限による分岐、ステータスコードの処理、オブジェクトの型による分岐、注文状態や決済状態の判定、APIレスポンスの分類などです。

1-4. 従来のswitch文だけでは書きにくい条件分岐

従来のswitch文だけでは、値そのものの一致は書きやすい一方で、範囲条件や複合条件は表現しにくい場合があります。

たとえば、点数によって評価を分けたい場合、古い書き方ではif文を使うことが多くなります。

C#
int score = 72;

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

もちろん、このコードは問題ありません。しかし条件が増えてくると、if elseが長くなり、どの条件がどの分岐に対応しているのか見通しが悪くなることがあります。

switch whenを使うと、同じ「判定対象」に対する分岐をひとまとまりにしやすくなります。

C#
switch (score)
{
case int n when n >= 80:
Console.WriteLine("A");
break;

case int n when n >= 60:
Console.WriteLine("B");
break;

default:
Console.WriteLine("C");
break;
}

このように、switch whenは「ある値を基準に、条件を段階的に判定したい」ときに使いやすい構文です。

2. C# switch whenの基本構文

2-1. switch文でwhenを使う書き方

switch文でwhenを使う基本構文は次のとおりです。

C#
switch (判定対象)
{
case パターン when 条件式:
// 条件に一致したときの処理
break;

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

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

C#
int age = 20;

switch (age)
{
case int n when n >= 20:
Console.WriteLine("成人です");
break;

case int n when n >= 0:
Console.WriteLine("未成年です");
break;

default:
Console.WriteLine("不正な年齢です");
break;
}

この例では、ageint nとして受け取り、whenで年齢の条件を判定しています。

2-2. case 条件 when 条件式 の読み方

case int n when n >= 20:は、最初は少し読みづらく感じるかもしれません。

分解すると、次のようになります。

C#
case int n when n >= 20:

int nは「判定対象がintとして扱えるなら、その値をnという変数に入れる」という意味です。

when n >= 20は「さらにnが20以上なら」という追加条件です。

つまり、全体としては「判定対象がintで、その値が20以上ならこのcaseを実行する」と読めます。

C#
object value = 25;

switch (value)
{
case int n when n >= 20:
Console.WriteLine($"{n}は20以上の整数です");
break;

case int n:
Console.WriteLine($"{n}は20未満の整数です");
break;

default:
Console.WriteLine("整数ではありません");
break;
}

whenを使うと、パターンで値を取り出し、その値に対して追加条件を書けます。

2-3. defaultとの組み合わせ方

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

C#
string command = "delete";

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

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

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

whenを使う場合でも、defaultは想定外のケースを受け止めるために重要です。

C#
int amount = -100;

switch (amount)
{
case int n when n > 0:
Console.WriteLine("正の金額です");
break;

case 0:
Console.WriteLine("金額は0です");
break;

default:
Console.WriteLine("不正な金額です");
break;
}

この例では、正の値と0を明示的に処理し、それ以外をdefaultで処理しています。

なお、defaultは通常「最後の受け皿」として書くのが読みやすいです。

2-4. 複数のcaseにwhenを付ける例

複数のcaseにそれぞれwhenを付けることもできます。

C#
int score = 75;
bool submittedOnTime = true;

switch (score)
{
case int n when n >= 80 && submittedOnTime:
Console.WriteLine("A評価です");
break;

case int n when n >= 60 && submittedOnTime:
Console.WriteLine("B評価です");
break;

case int n when n >= 60 && !submittedOnTime:
Console.WriteLine("遅延提出のためC評価です");
break;

default:
Console.WriteLine("不合格です");
break;
}

このように、同じ判定対象に対して、追加条件を変えながら分岐できます。

ただし、条件が多すぎると読みにくくなるため、複雑な条件はメソッド化することも検討しましょう。

C#
static bool IsHighScore(int score, bool submittedOnTime)
{
return score >= 80 && submittedOnTime;
}

2-5. 実行されるcaseは上から順に判定される

switch文のcaseは、上から順に判定されます。最初に一致したcaseが実行されるため、順番は非常に重要です。C#のswitch文は、最初に一致したパターンと、存在する場合はtrueになったガード条件の組み合わせを実行します。Microsoft Learn+1

たとえば、次のコードを見てください。

C#
int score = 90;

switch (score)
{
case int n when n >= 60:
Console.WriteLine("合格です");
break;

case int n when n >= 80:
Console.WriteLine("高得点です");
break;

default:
Console.WriteLine("不合格です");
break;
}

この場合、scoreは90なのでn >= 80も満たします。しかし先にn >= 60に一致するため、出力されるのは「合格です」です。

期待どおりに「高得点です」を出したい場合は、条件が狭いものを先に書きます。

C#
switch (score)
{
case int n when n >= 80:
Console.WriteLine("高得点です");
break;

case int n when n >= 60:
Console.WriteLine("合格です");
break;

default:
Console.WriteLine("不合格です");
break;
}

switch whenでは、条件の順番が結果に直結します。範囲条件を書くときは、より具体的な条件を先に置くのが基本です。

3. C# switch whenの実例

3-1. 数値の範囲で分岐する例

switch whenの代表的な使い方が、数値の範囲による分岐です。

C#
int score = 88;

switch (score)
{
case int n when n >= 90:
Console.WriteLine("評価: S");
break;

case int n when n >= 80:
Console.WriteLine("評価: A");
break;

case int n when n >= 70:
Console.WriteLine("評価: B");
break;

case int n when n >= 60:
Console.WriteLine("評価: C");
break;

case int n when n >= 0:
Console.WriteLine("評価: D");
break;

default:
Console.WriteLine("点数が不正です");
break;
}

このコードでは、点数が高い条件から順に判定しています。

数値の範囲分岐では、順番を間違えると意図しない結果になります。たとえばn >= 0を先頭に書くと、0以上の値がすべてそこで一致してしまいます。

3-2. 文字列の内容で分岐する例

文字列に対してもwhenを使えます。

C#
string input = "admin_user";

switch (input)
{
case string s when s.StartsWith("admin"):
Console.WriteLine("管理者関連のユーザーです");
break;

case string s when s.Contains("guest"):
Console.WriteLine("ゲストユーザーです");
break;

case "":
Console.WriteLine("空文字です");
break;

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

default:
Console.WriteLine("通常ユーザーです");
break;
}

StartsWithContainsのようなメソッドをwhenの条件式で使うと、文字列の内容に応じた分岐が書けます。

ただし、case string s when s.StartsWith("admin"):は、inputnullの場合には一致しません。nullを特別に扱いたい場合は、case null:を用意すると安全です。

3-3. enumとwhenを組み合わせる例

enumwhenを組み合わせると、状態管理の分岐を整理しやすくなります。

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

OrderStatus status = OrderStatus.Paid;
bool isStockAvailable = false;

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

case OrderStatus.Paid when isStockAvailable:
Console.WriteLine("発送準備を開始します");
break;

case OrderStatus.Paid when !isStockAvailable:
Console.WriteLine("在庫切れのため発送できません");
break;

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

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

default:
Console.WriteLine("不明な注文状態です");
break;
}

この例では、OrderStatus.Paidという同じ状態でも、在庫があるかどうかで処理を分けています。

通常のswitchだけだとPaidの中にさらにifを書く必要がありますが、whenを使うとcaseの段階で条件を分けられます。

3-4. nullチェックを含めた分岐例

switch whenでは、nullチェックを含めた分岐も書けます。

C#
string? name = null;

switch (name)
{
case null:
Console.WriteLine("名前がnullです");
break;

case string s when s.Length == 0:
Console.WriteLine("名前が空文字です");
break;

case string s when s.Length < 3:
Console.WriteLine("名前が短すぎます");
break;

default:
Console.WriteLine("有効な名前です");
break;
}

nullと空文字は意味が違います。

nullは「値が存在しない」状態です。一方、空文字は「文字列としては存在するが、中身が空」の状態です。

この違いを明確に分けたいとき、switchcase null:case string s when s.Length == 0:を分けて書くと読みやすくなります。

3-5. 複数条件を&&・||で組み合わせる例

whenの条件式では、通常のif文と同じように&&||を使えます。

C#
int age = 25;
bool hasCoupon = true;
bool isMember = false;

switch (age)
{
case int n when n >= 20 && hasCoupon:
Console.WriteLine("成人向けクーポン割引を適用します");
break;

case int n when n >= 20 || isMember:
Console.WriteLine("通常割引を適用します");
break;

default:
Console.WriteLine("割引対象外です");
break;
}

&&は「かつ」、||は「または」です。

複数条件を組み合わせると柔軟な分岐ができますが、条件が長くなると可読性が下がります。

C#
case int n when n >= 20 && hasCoupon && isMember && IsCampaignPeriod():

このように長くなりすぎる場合は、条件をメソッドに切り出すと読みやすくなります。

C#
case int n when IsDiscountTarget(n, hasCoupon, isMember):
Console.WriteLine("割引を適用します");
break;

4. switch whenとif文の違い

4-1. if文が向いているケース

if文は、自由度の高い条件分岐に向いています。

たとえば、判定対象が複数あり、それぞれの条件が独立している場合はif文のほうが自然です。

C#
if (user == null)
{
Console.WriteLine("ユーザーが存在しません");
}
else if (!user.IsActive)
{
Console.WriteLine("ユーザーが無効です");
}
else if (DateTime.Now.Hour < 9)
{
Console.WriteLine("営業時間外です");
}
else
{
Console.WriteLine("ログインできます");
}

このように、userIsActive、現在時刻など、複数の要素を自由に判定する場合はif文がわかりやすいです。

if文は、条件式をそのまま上から読めるため、単純な分岐ではもっとも直感的です。

4-2. switch whenが向いているケース

switch whenは、ひとつの値やオブジェクトを中心に分岐したい場合に向いています。

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

case OrderStatus.Paid when order.Amount >= 10000:
Console.WriteLine("高額注文として処理します");
break;

case OrderStatus.Paid:
Console.WriteLine("通常注文として処理します");
break;

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

このコードでは、order.Statusというひとつの判定対象を軸に処理を分けています。

switch whenは、「何について分岐しているのか」が明確になりやすいのがメリットです。

4-3. 可読性で比較するif文とswitch when

次のようなif elseのコードを見てみましょう。

C#
if (status == OrderStatus.Paid && amount >= 10000)
{
Console.WriteLine("高額支払い済み注文");
}
else if (status == OrderStatus.Paid)
{
Console.WriteLine("支払い済み注文");
}
else if (status == OrderStatus.Pending)
{
Console.WriteLine("支払い待ち注文");
}
else if (status == OrderStatus.Cancelled)
{
Console.WriteLine("キャンセル注文");
}
else
{
Console.WriteLine("不明な注文");
}

これをswitch whenで書くと、次のようになります。

C#
switch (status)
{
case OrderStatus.Paid when amount >= 10000:
Console.WriteLine("高額支払い済み注文");
break;

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

case OrderStatus.Pending:
Console.WriteLine("支払い待ち注文");
break;

case OrderStatus.Cancelled:
Console.WriteLine("キャンセル注文");
break;

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

switch whenでは、statusに関する分岐であることが最初にわかります。

分岐の軸がひとつに決まっている場合は、switch whenのほうが読みやすくなることが多いです。

4-4. 複雑な条件分岐を整理しやすい理由

switch whenが複雑な条件分岐を整理しやすい理由は、パターンと追加条件を分けて書けるからです。

C#
case OrderStatus.Paid when amount >= 10000:

この1行では、次の2つを分けて表現しています。

OrderStatus.Paidという基本条件と、amount >= 10000という追加条件です。

if文で書くと、すべてが1つの条件式にまとまります。

C#
if (status == OrderStatus.Paid && amount >= 10000)

もちろん、この程度ならif文でも問題ありません。しかし分岐が増えると、switch whenのほうが条件のグルーピングがしやすくなります。

4-5. 無理にswitch whenへ置き換えないほうがよいケース

switch whenは便利ですが、すべてのif文を置き換える必要はありません。

次のような場合は、if文のほうが読みやすいです。

C#
if (user == null)
{
return;
}

if (!user.IsActive)
{
return;
}

if (!user.HasPermission)
{
return;
}

このようなガード句は、switch whenにするよりもifで早期リターンしたほうが自然です。

また、判定対象が明確にひとつではない場合も、if文のほうが向いています。

C#
if (user.IsActive && order.IsPaid && stock.Count > 0)
{
Console.WriteLine("発送できます");
}

switch whenは、分岐の軸がはっきりしているときに使うのが効果的です。無理に使うと、かえって読みにくいコードになります。

5. switch whenとパターンマッチング

5-1. C#のパターンマッチングとは

C#のパターンマッチングとは、値が特定の形や型、条件に一致するかを判定し、一致した場合に変数として取り出せる機能です。MicrosoftのC#リファレンスでは、is式、switch文、switch式で、定数パターン、型パターン、プロパティパターン、リレーショナルパターン、破棄パターンなど複数のパターンを使用できると説明されています。Microsoft Learn+1

たとえば、次のコードではobject型の値がintかどうかを判定し、一致した場合はnumberとして使っています。

C#
object value = 123;

if (value is int number)
{
Console.WriteLine(number * 2);
}

switch文でも同じように、型や値のパターンに応じて分岐できます。

C#
object value = "hello";

switch (value)
{
case int number:
Console.WriteLine($"整数: {number}");
break;

case string text:
Console.WriteLine($"文字列: {text}");
break;

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

whenは、このパターンマッチングに追加条件を付けるためによく使われます。

5-2. 型パターンとwhenを組み合わせる

型パターンとwhenを組み合わせると、「特定の型で、さらに条件を満たす場合」という分岐が書けます。

C#
object value = 150;

switch (value)
{
case int n when n >= 100:
Console.WriteLine("100以上の整数です");
break;

case int n:
Console.WriteLine("100未満の整数です");
break;

case string s when s.Length >= 10:
Console.WriteLine("10文字以上の文字列です");
break;

case string s:
Console.WriteLine("短い文字列です");
break;

default:
Console.WriteLine("対象外の型です");
break;
}

この例では、intstringで分岐し、さらにwhenで値の条件を追加しています。

型に応じて処理を変える場面では、if (value is int n)を何度も書くよりも、switchにまとめたほうが読みやすくなる場合があります。

5-3. プロパティパターンとwhenを組み合わせる

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

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

User user = new User
{
Role = "Admin",
Age = 30,
IsActive = true
};

switch (user)
{
case { Role: "Admin" } u when u.IsActive:
Console.WriteLine("有効な管理者です");
break;

case { Role: "Admin" }:
Console.WriteLine("無効な管理者です");
break;

case { Age: >= 20 }:
Console.WriteLine("成人ユーザーです");
break;

default:
Console.WriteLine("その他のユーザーです");
break;
}

case { Role: "Admin" } u when u.IsActive:は、「RoleAdminで、さらにIsActivetrueの場合」と読めます。

プロパティパターンだけでも条件を書けますが、whenを使うと、より複雑な条件やメソッド呼び出しを追加できます。

5-4. リレーショナルパターンとの違い

C#では、>=<などを使ったリレーショナルパターンも使えます。

C#
int score = 85;

string result = score switch
{
>= 80 => "A",
>= 60 => "B",
_ => "C"
};

このように、単純な大小比較だけなら、whenを使わなくても書けます。

whenを使った場合は次のようになります。

C#
string result = score switch
{
int n when n >= 80 => "A",
int n when n >= 60 => "B",
_ => "C"
};

どちらも似た結果になりますが、単純な範囲条件ならリレーショナルパターンのほうが短く書けます。

一方で、次のようにメソッド呼び出しや複数の変数を使う場合はwhenが便利です。

C#
string result = score switch
{
int n when n >= 80 && IsSubmittedOnTime() => "A",
int n when n >= 60 => "B",
_ => "C"
};

リレーショナルパターンは「値そのものの比較」に強く、whenは「追加条件を自由に書く」場面に向いています。

5-5. and・or・notパターンとの使い分け

C#では、andornotを使ってパターンを組み合わせることもできます。公式ドキュメントでも、複数のパターンをandornotで結合できると説明されています。Microsoft Learn+1

C#
int score = 75;

string result = score switch
{
>= 80 and <= 100 => "A",
>= 60 and < 80 => "B",
>= 0 and < 60 => "C",
_ => "不正な点数"
};

これはwhenを使わずに、パターンだけで範囲を表現しています。

一方、whenを使うと次のようになります。

C#
string result = score switch
{
int n when n >= 80 && n <= 100 => "A",
int n when n >= 60 && n < 80 => "B",
int n when n >= 0 && n < 60 => "C",
_ => "不正な点数"
};

単純な範囲判定なら、andorを使ったパターンのほうが簡潔です。

ただし、外部変数やメソッドを含む条件ではwhenが使いやすくなります。

C#
string result = score switch
{
int n when n >= 80 && IsBonusTarget(n) => "A+",
>= 80 => "A",
>= 60 => "B",
_ => "C"
};

目安として、パターンだけで自然に書けるならandornotを使い、条件式として書いたほうが自然ならwhenを使うとよいでしょう。

6. switch式でwhenを使う方法

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

C#には、従来のswitch文に加えて、値を返すためのswitch式があります。switch式は、入力式を複数の候補と照合し、対応する式の結果を返す構文です。Microsoft Learn

switch文は、処理を実行するために使います。

C#
switch (status)
{
case "OK":
Console.WriteLine("成功");
break;

default:
Console.WriteLine("失敗");
break;
}

一方、switch式は値を返すために使います。

C#
string message = status switch
{
"OK" => "成功",
_ => "失敗"
};

switch式では、casebreakを書かず、=>で結果を返します。

6-2. switch式にwhen条件を付ける構文

switch式でもwhenを使えます。

C#
int score = 85;

string grade = score switch
{
int n when n >= 90 => "S",
int n when n >= 80 => "A",
int n when n >= 70 => "B",
int n when n >= 60 => "C",
_ => "D"
};

Console.WriteLine(grade);

switch式でのwhenは、次のように読みます。

C#
int n when n >= 90 => "S"

これは、「判定対象をint nとして扱い、n >= 90なら"S"を返す」という意味です。

switch式は戻り値を作る目的に向いています。

6-3. 戻り値を返す条件分岐の書き方

たとえば、注文金額に応じて送料を返すメソッドを考えます。

C#
static int GetShippingFee(int amount, bool isPremiumMember)
{
return amount switch
{
int n when n >= 5000 => 0,
int n when isPremiumMember && n >= 3000 => 0,
int n when n > 0 => 500,
_ => throw new ArgumentException("注文金額が不正です")
};
}

このコードでは、条件に応じて送料を返しています。

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

C#
static int GetShippingFee(int amount, bool isPremiumMember)
{
if (amount >= 5000)
{
return 0;
}

if (isPremiumMember && amount >= 3000)
{
return 0;
}

if (amount > 0)
{
return 500;
}

throw new ArgumentException("注文金額が不正です");
}

どちらも正しい書き方です。

戻り値を作るだけならswitch式、途中で複数の処理を実行するならswitch文やif文が向いています。

6-4. switch式で例外を返す・投げるパターン

switch式では、想定外の値に対して例外を投げる書き方もよく使われます。

C#
enum PaymentStatus
{
Unpaid,
Paid,
Refunded
}

static string GetPaymentMessage(PaymentStatus status)
{
return status switch
{
PaymentStatus.Unpaid => "未払いです",
PaymentStatus.Paid => "支払い済みです",
PaymentStatus.Refunded => "返金済みです",
_ => throw new ArgumentOutOfRangeException(nameof(status), "不明な支払い状態です")
};
}

_は破棄パターンで、「それ以外」を表します。

defaultの代わりに_を使うイメージです。

whenを組み合わせることもできます。

C#
static string GetDiscountMessage(int amount, bool isCampaign)
{
return amount switch
{
int n when n < 0 => throw new ArgumentException("金額が不正です"),
int n when isCampaign && n >= 10000 => "キャンペーン特別割引",
int n when n >= 5000 => "通常割引",
_ => "割引なし"
};
}

入力値が不正な場合は早めに例外を投げ、正常な分岐をその下に書くと読みやすくなります。

6-5. switch文とswitch式のどちらを選ぶべきか

switch文とswitch式は、目的によって使い分けます。

処理を実行したい場合はswitch文が向いています。

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

case "delete":
Delete();
break;

default:
ShowError();
break;
}

値を返したい場合はswitch式が向いています。

C#
string label = status switch
{
"open" => "対応中",
"closed" => "完了",
_ => "不明"
};

switch式は簡潔ですが、複雑な処理を詰め込むと読みにくくなります。

複数行の処理、ログ出力、外部処理の呼び出し、状態変更などがある場合は、無理にswitch式にせず、switch文を使うほうが安全です。

7. C# switch whenでよくあるエラーと注意点

7-1. caseの順番によって条件が到達不能になる

switch whenでよくあるミスが、広い条件を先に書いてしまうことです。

C#
int score = 90;

switch (score)
{
case int n when n >= 0:
Console.WriteLine("有効な点数です");
break;

case int n when n >= 80:
Console.WriteLine("高得点です");
break;
}

この例では、n >= 80の値も先にn >= 0に一致してしまいます。そのため、後ろのn >= 80は実行されません。

C#コンパイラは、到達できないcaseがある場合にエラーを出すことがあります。公式ドキュメントでも、上位のcaseがすでに処理している場合や、パターンが一致不可能な場合に、到達不能なcaseとしてエラーになると説明されています。Microsoft Learn

正しくは、狭い条件を先に書きます。

C#
switch (score)
{
case int n when n >= 80:
Console.WriteLine("高得点です");
break;

case int n when n >= 0:
Console.WriteLine("有効な点数です");
break;
}

7-2. when条件が常にtrue・falseになる

whenの条件が常にtrueまたは常にfalseになると、意図しないコードになります。

C#
int age = 25;

switch (age)
{
case int n when n >= 0 || n < 0:
Console.WriteLine("常に一致します");
break;

default:
Console.WriteLine("ここには来ません");
break;
}

n >= 0 || n < 0は、整数であれば常にtrueです。

逆に、次のような条件は常にfalseです。

C#
case int n when n > 100 && n < 0:

100より大きく、同時に0より小さい整数は存在しません。

このような条件はバグの原因になります。whenの条件は、単体で見ても意味がわかるように書くことが大切です。

7-3. nullの扱いで意図しない分岐になる

nullを扱う場合は、case null:を明示的に書くと安全です。

C#
string? text = null;

switch (text)
{
case string s when s.Length > 0:
Console.WriteLine("文字列があります");
break;

default:
Console.WriteLine("空またはnullです");
break;
}

このコードでは、textnullの場合、case string sには一致せず、defaultに進みます。

問題がない場合もありますが、nullと空文字を分けたいなら次のように書きます。

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

case "":
Console.WriteLine("空文字です");
break;

case string s:
Console.WriteLine($"文字列: {s}");
break;
}

nullを「想定外」として扱うのか、「正常な入力の一種」として扱うのかを決めておくことが重要です。

7-4. break・returnを書き忘れる

switch文では、各caseの終わりにbreakreturnなどを書きます。

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

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

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

C#
static string GetMessage(string command)
{
switch (command)
{
case "start":
return "開始します";

case "stop":
return "停止します";

default:
return "不明なコマンドです";
}
}

switch式の場合は、breakを書きません。

C#
string message = command switch
{
"start" => "開始します",
"stop" => "停止します",
_ => "不明なコマンドです"
};

switch文とswitch式では書き方が異なるため、混同しないようにしましょう。

7-5. 条件を詰め込みすぎて読みにくくなる

whenには自由に条件式を書けますが、何でも詰め込むと読みにくくなります。

C#
case Order o when o.Amount >= 10000 && o.User.IsActive && o.User.Role == "Admin" && DateTime.Now.DayOfWeek != DayOfWeek.Sunday:

このような条件は、一目で意味を理解しにくいです。

メソッドに切り出すと、意図が明確になります。

C#
case Order o when CanApplySpecialDiscount(o):
Console.WriteLine("特別割引を適用します");
break;
C#
static bool CanApplySpecialDiscount(Order order)
{
return order.Amount >= 10000
&& order.User.IsActive
&& order.User.Role == "Admin"
&& DateTime.Now.DayOfWeek != DayOfWeek.Sunday;
}

whenの条件は、長くても1行で意味が伝わる程度に抑えるのが理想です。

8. 実務で使えるswitch whenの設計例

8-1. 入力値バリデーションで使う例

入力値のバリデーションは、switch whenを使いやすい場面です。

C#
static string ValidatePassword(string? password)
{
switch (password)
{
case null:
return "パスワードを入力してください";

case string s when s.Length < 8:
return "パスワードは8文字以上で入力してください";

case string s when !s.Any(char.IsDigit):
return "パスワードには数字を含めてください";

case string s when !s.Any(char.IsUpper):
return "パスワードには大文字を含めてください";

default:
return "有効なパスワードです";
}
}

このコードでは、passwordに対するチェックを上から順に実行しています。

バリデーションでは、エラーメッセージを返す順番も重要です。ユーザーに最初に伝えたいエラーを上に書くと、挙動がわかりやすくなります。

8-2. ステータスコードや状態管理で使う例

APIのステータスコードや業務ステータスの分岐にもswitch whenは便利です。

C#
int statusCode = 404;
bool isRetryable = false;

switch (statusCode)
{
case int code when code >= 200 && code < 300:
Console.WriteLine("成功レスポンスです");
break;

case 400:
Console.WriteLine("リクエストが不正です");
break;

case 401:
case 403:
Console.WriteLine("認証または権限エラーです");
break;

case 404 when isRetryable:
Console.WriteLine("再試行可能な未検出エラーです");
break;

case 404:
Console.WriteLine("リソースが見つかりません");
break;

case int code when code >= 500:
Console.WriteLine("サーバーエラーです");
break;

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

ステータスコードのように範囲と個別値が混ざる分岐では、switch whenを使うと整理しやすくなります。

8-3. ユーザー権限による処理分岐

ユーザー権限の処理では、ロールだけでなく、アカウント状態や契約状態も条件になることがあります。

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

static string GetAccessiblePage(AppUser user)
{
switch (user.Role)
{
case "Admin" when user.IsActive:
return "管理画面";

case "Admin":
return "管理者アカウントが無効です";

case "Member" when user.IsActive && user.IsPaidPlan:
return "有料会員ページ";

case "Member" when user.IsActive:
return "無料会員ページ";

case "Guest":
return "ゲストページ";

default:
return "アクセスできません";
}
}

Roleを分岐の軸にしながら、whenで追加条件を指定しています。

このように書くと、「どのロールに対して、どんな条件があるのか」を一覧しやすくなります。

8-4. オブジェクトの型やプロパティによる分岐

実務では、基底クラスやインターフェース型の変数に対して、実際の型ごとに処理を分けることがあります。

C#
abstract class Notification { }

class EmailNotification : Notification
{
public string Email { get; set; } = "";
public bool IsHtml { get; set; }
}

class SmsNotification : Notification
{
public string PhoneNumber { get; set; } = "";
}

class PushNotification : Notification
{
public string DeviceToken { get; set; } = "";
public bool IsEnabled { get; set; }
}

static void Send(Notification notification)
{
switch (notification)
{
case EmailNotification email when email.IsHtml:
Console.WriteLine($"HTMLメールを送信: {email.Email}");
break;

case EmailNotification email:
Console.WriteLine($"テキストメールを送信: {email.Email}");
break;

case SmsNotification sms:
Console.WriteLine($"SMSを送信: {sms.PhoneNumber}");
break;

case PushNotification push when push.IsEnabled:
Console.WriteLine($"プッシュ通知を送信: {push.DeviceToken}");
break;

case PushNotification:
Console.WriteLine("プッシュ通知は無効です");
break;

default:
Console.WriteLine("未対応の通知です");
break;
}
}

型パターンとwhenを組み合わせることで、型ごとの処理と追加条件を自然に書けます。

8-5. if elseの連続をswitch whenへリファクタリングする例

次のようなif elseがあるとします。

C#
if (order.Status == OrderStatus.Paid && order.Amount >= 10000)
{
Console.WriteLine("高額注文として処理");
}
else if (order.Status == OrderStatus.Paid)
{
Console.WriteLine("通常注文として処理");
}
else if (order.Status == OrderStatus.Pending)
{
Console.WriteLine("支払い待ち");
}
else if (order.Status == OrderStatus.Cancelled)
{
Console.WriteLine("キャンセル済み");
}
else
{
Console.WriteLine("不明な状態");
}

これをswitch whenにリファクタリングすると、次のようになります。

C#
switch (order.Status)
{
case OrderStatus.Paid when order.Amount >= 10000:
Console.WriteLine("高額注文として処理");
break;

case OrderStatus.Paid:
Console.WriteLine("通常注文として処理");
break;

case OrderStatus.Pending:
Console.WriteLine("支払い待ち");
break;

case OrderStatus.Cancelled:
Console.WriteLine("キャンセル済み");
break;

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

order.Statusを軸にして分岐していることが明確になりました。

リファクタリングのポイントは、最初に「分岐の主役」を見つけることです。この例ではorder.Statusが主役です。そこに対して、Amountのような追加条件をwhenで付けています。

9. switch whenを読みやすく書くコツ

9-1. 条件の優先順位を明確にする

switch whenでは、上から順にcaseが評価されます。そのため、条件の優先順位を明確にすることが大切です。

C#
switch (score)
{
case int n when n >= 90:
return "S";

case int n when n >= 80:
return "A";

case int n when n >= 70:
return "B";

default:
return "C";
}

このように、範囲条件では狭い条件から広い条件へ並べると自然です。

逆に、広い条件を先に書くと、後ろの条件に到達しないことがあります。

C#
case int n when n >= 0:
case int n when n >= 80:

このような順番は避けましょう。

9-2. 複雑な条件はメソッド化する

whenの中に複雑な条件を書きすぎると、分岐の意図が見えにくくなります。

C#
case User u when u.IsActive && u.LastLoginAt >= DateTime.Today.AddDays(-30) && u.Point >= 1000:

このような場合は、条件をメソッド化します。

C#
case User u when IsPremiumActiveUser(u):
Console.WriteLine("アクティブな優良ユーザーです");
break;
C#
static bool IsPremiumActiveUser(User user)
{
return user.IsActive
&& user.LastLoginAt >= DateTime.Today.AddDays(-30)
&& user.Point >= 1000;
}

メソッド名で条件の意味を表現すれば、switch全体が読みやすくなります。

9-3. defaultまたは_で想定外ケースを処理する

switch文ではdefaultswitch式では_を使って、想定外のケースを処理します。

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

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

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

switch式では次のように書きます。

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

想定外ケースを放置すると、将来enumに値が追加されたときや、不正な値が入ったときにバグになりやすいです。

業務上ありえない値であれば、例外を投げる設計も有効です。

C#
string message = status switch
{
OrderStatus.Pending => "支払い待ち",
OrderStatus.Paid => "支払い済み",
_ => throw new ArgumentOutOfRangeException(nameof(status))
};

9-4. 条件分岐の責務を増やしすぎない

switch whenは便利ですが、1つのswitchに多くの責務を持たせすぎると読みにくくなります。

たとえば、次のような処理を1つのswitchにまとめすぎるのは避けたほうがよいです。

注文状態の判定、在庫チェック、ポイント付与、メール送信、ログ出力、例外処理などです。

これらをすべて1つのswitchに詰め込むと、条件分岐というより巨大な業務ロジックになります。

C#
switch (order.Status)
{
case OrderStatus.Paid when order.Amount >= 10000:
ApplyDiscount(order);
AddPoint(order);
SendMail(order);
WriteLog(order);
break;
}

このような場合は、分岐先の処理をメソッドやクラスに分けるとよいです。

C#
switch (order.Status)
{
case OrderStatus.Paid when order.Amount >= 10000:
ProcessHighValuePaidOrder(order);
break;
}

switch whenは、分岐を整理するための構文です。すべての業務処理を詰め込む場所ではありません。

9-5. チーム開発で統一した書き方にする

チーム開発では、switch whenの書き方を統一すると保守しやすくなります。

たとえば、次のようなルールを決めておくとよいでしょう。

範囲条件は狭い順に書く、defaultまたは_を必ず書く、複雑なwhen条件はメソッド化する、switch式は値を返す場合に使う、複数行の処理はswitch文を使う、などです。

同じ処理でも、開発者によって書き方がばらばらだと読みづらくなります。

C#
// チーム内で統一しやすい書き方
return score switch
{
>= 90 => "S",
>= 80 => "A",
>= 70 => "B",
>= 60 => "C",
_ => "D"
};
C#
// 複雑な条件はメソッド化
return user switch
{
User u when IsPremiumActiveUser(u) => "優良ユーザー",
User u when u.IsActive => "通常ユーザー",
_ => "対象外"
};

コードの可読性は、個人の好みだけでなく、チーム全体の一貫性にも左右されます。

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

10-1. switch whenはどのC#バージョンから使える?

switchでのパターンマッチングやwhenによるガード条件は、C# 7.0以降の文脈で使われるようになりました。C# 7.0は2017年3月にVisual Studio 2017とともにリリースされ、主な新機能の1つとしてパターンマッチングが含まれています。Microsoft Learn

ただし、switch式、プロパティパターン、リレーショナルパターン、andornotパターンなどは、C#のバージョンによって利用可否が異なります。

古いプロジェクトで使えない場合は、プロジェクトのLangVersionやターゲットフレームワーク、使用しているSDKのバージョンを確認しましょう。

10-2. switch式でもwhenは使える?

はい、switch式でもwhenは使えます。

C#
int score = 85;

string grade = score switch
{
int n when n >= 80 => "A",
int n when n >= 60 => "B",
_ => "C"
};

ただし、単純な範囲比較なら、リレーショナルパターンを使ったほうが簡潔です。

C#
string grade = score switch
{
>= 80 => "A",
>= 60 => "B",
_ => "C"
};

メソッド呼び出しや外部変数を使う条件では、whenが役立ちます。

C#
string grade = score switch
{
int n when n >= 80 && submittedOnTime => "A",
int n when n >= 60 => "B",
_ => "C"
};

10-3. whenとifはどちらが速い?

多くの実務コードでは、whenifの速度差を気にするより、可読性と保守性を優先したほうがよいです。

whenは魔法のように処理を高速化する構文ではありません。条件分岐として見ると、if文と同じように条件を評価します。

重要なのは、どちらが処理の意図を読み取りやすいかです。

ひとつの値を軸に分岐するならswitch when、複数の条件を自由に組み合わせるならif文が向いています。

パフォーマンスが本当に問題になる場合は、推測ではなくベンチマークを取って判断しましょう。

10-4. whenの中でメソッドを呼んでもよい?

はい、whenの中でメソッドを呼ぶことはできます。

C#
switch (user)
{
case User u when IsPremiumUser(u):
Console.WriteLine("プレミアムユーザーです");
break;

default:
Console.WriteLine("通常ユーザーです");
break;
}

ただし、注意点があります。

whenの中で呼ぶメソッドは、できるだけ副作用のないものにしましょう。

たとえば、条件判定の中でデータベース更新、ファイル書き込み、メール送信などを行うと、コードの流れがわかりにくくなります。

C#
case User u when UpdateUserAndReturnResult(u):

このような書き方は避けたほうがよいです。

whenの中では、基本的に「判定だけ」を行うのが安全です。

10-5. defaultにwhenは付けられる?

通常、whencaseに付けて使います。

C#
case int n when n > 0:

defaultは「どのcaseにも一致しなかった場合」の受け皿として使うため、追加条件を付けたい場合は、case var x when 条件:のように書くのが一般的です。

C#
object value = 123;

switch (value)
{
case var x when x != null:
Console.WriteLine("null以外です");
break;

default:
Console.WriteLine("nullです");
break;
}

switch式でも同じ考え方で、_ when 条件のように書けます。

C#
string result = value switch
{
_ when value != null => "null以外",
_ => "null"
};

ただし、_ when 条件を多用すると、何を基準に分岐しているのかわかりにくくなることがあります。できるだけ具体的なパターンと組み合わせて使いましょう。

10-6. switch whenとcase whenの違いは?

厳密には、C#の構文としてよく使われる言い方は「switchwhenを使う」「casewhenを付ける」です。

switch whenという単独の構文があるというより、switch文やswitch式の中で、caseまたはパターンに対してwhen条件を追加するイメージです。

C#
switch (value)
{
case int n when n > 0:
Console.WriteLine("正の整数です");
break;
}

このコードでは、switchの中にあるcase int nに対して、when n > 0という追加条件を付けています。

そのため、検索では「C# switch when」と呼ばれることが多いですが、実際には「caseにwhenガードを付ける」と理解すると正確です。

まとめ

C#のswitch whenは、switch文やswitch式の分岐に追加条件を付けられる便利な構文です。

通常のswitchでは値やパターンの一致を判定しますが、whenを使うと「一致したうえで、さらに条件を満たす場合」という分岐を書けます。

数値の範囲、文字列の内容、enumの状態、nullチェック、ユーザー権限、オブジェクトの型やプロパティなど、実務でよくある条件分岐を整理しやすくなります。

一方で、switch whenは万能ではありません。

単純な条件ならif文のほうが読みやすいこともあります。戻り値を作るだけならswitch式が向いています。単純な範囲比較ならリレーショナルパターンやandorパターンのほうが簡潔に書ける場合もあります。

大切なのは、分岐の軸を明確にすることです。

ひとつの値や状態を中心に分岐するならswitch when、複数の条件を自由に組み合わせるならif文、値を返すだけならswitch式というように使い分けると、読みやすく保守しやすいC#コードになります。

switch whenを書くときは、条件の順番、nullの扱い、defaultまたは_の用意、複雑な条件のメソッド化を意識しましょう。これらを押さえておけば、if elseの連続で読みにくくなった条件分岐を、よりわかりやすく整理できます。