C#という言語は、バージョンを重ねるごとに表現力が飛躍的に向上してきました。

その中でも、条件分岐の柔軟性を劇的に高めたのが「パターンマッチング」の導入です。

かつての switch 文は、整数や文字列などの定数との完全一致を比較するだけの限定的な機能しか持っていませんでしたが、現代のC#においては、「when句(ケースガード)」を用いることで、複雑な条件論理を簡潔かつ読みやすく記述することが可能になっています。

この記事では、C#における switch 文およびスイッチ式における when 句の使い方を、基礎から応用まで詳しく解説します。

複雑な if-else の連鎖をどのように整理し、保守性の高いコードへと昇華させるかを学んでいきましょう。

switch文におけるwhen句の基本概念

C# 7.0から導入された when 句は、case ラベルにおいて追加の論理条件を指定するための仕組みです。

これを「ケースガード」と呼びます。

パターンが一致したとしても、when 句で指定された ブール条件が true を返さない限り、その case ブロックは実行されません

従来の switch 文では、変数が特定の値であるかどうかしか判定できませんでしたが、when 句の登場により、「型が一致し、かつ特定のプロパティが条件を満たす場合」といった高度な判定を一つの case で完結させることができます。

基本的な構文は以下の通りです。

C#
switch (オブジェクト)
{
    case 型 変数名 when 条件式:
        // 条件に一致した場合の処理
        break;
}

この構文の最大の利点は、「条件分岐の意図が明確になる」 ことにあります。

ネストされた if 文を排除し、フラットな構造で多分岐を記述できるため、可読性が大幅に向上します。

when句を使用した具体的な実装例

それでは、具体的なコード例を通じて when 句の利便性を確認していきましょう。

まずは数値の判定を行うシンプルな例から紹介します。

数値の範囲による条件分岐

特定の数値がどの範囲に属しているかに応じてメッセージを変える処理を考えます。

C# 9.0以降ではリレーショナルパターンも使えますが、when 句を使うことでより複雑な外部変数を参照した比較などが容易になります。

C#
using System;

public class Program
{
    public static void Main()
    {
        CheckTemperature(25);
        CheckTemperature(35);
        CheckTemperature(-5);
    }

    public static void CheckTemperature(int temp)
    {
        // switch文とwhen句の組み合わせ
        switch (temp)
        {
            case int t when t >= 30:
                Console.WriteLine($"{t}度は猛暑日、またはそれに近い暑さです。");
                break;
            case int t when t >= 10 && t < 30:
                Console.WriteLine($"{t}度は過ごしやすい気温です。");
                break;
            case int t when t < 10:
                Console.WriteLine($"{t}度は寒いです。対策をしてください。");
                break;
            default:
                Console.WriteLine("無効な気温データです。");
                break;
        }
    }
}
実行結果
25度は過ごしやすい気温です。
35度は猛暑日、またはそれに近い暑さです。
-5度は寒いです。対策をしてください。

上記のコードでは、case int t という型パターンで値を取得し、それを when 以降の条件式で評価しています。

これにより、値の範囲に基づいた分岐が直感的に記述できています。

オブジェクトのプロパティを組み合わせた判定

when 句が真価を発揮するのは、クラスや構造体のプロパティに基づいた複雑な判定 を行う場合です。

例えば、注文管理システムにおいて、注文の内容(金額や会員ステータス)によって割引率を変えるようなロジックを作成する場合を想定してみましょう。

C#
using System;

public class Order
{
    public decimal Amount { get; set; }
    public bool IsPremiumMember { get; set; }
    public string Category { get; set; } = string.Empty;
}

public class Program
{
    public static void Main()
    {
        var order1 = new Order { Amount = 15000, IsPremiumMember = true, Category = "Electronics" };
        var order2 = new Order { Amount = 8000, IsPremiumMember = true, Category = "Food" };
        var order3 = new Order { Amount = 20000, IsPremiumMember = false, Category = "Electronics" };

        PrintDiscount(order1);
        PrintDiscount(order2);
        PrintDiscount(order3);
    }

    public static void PrintDiscount(Order order)
    {
        switch (order)
        {
            // プレミアム会員かつ1万円以上の注文
            case Order o when o.IsPremiumMember && o.Amount >= 10000:
                Console.WriteLine($"カテゴリ:{o.Category} - プレミアム特典: 20%割引適用");
                break;

            // プレミアム会員、または全ユーザー対象の2万円以上の高額注文
            case Order o when o.IsPremiumMember || o.Amount >= 20000:
                Console.WriteLine($"カテゴリ:{o.Category} - 通常特典: 10%割引適用");
                break;

            // それ以外の注文
            case Order o:
                Console.WriteLine($"カテゴリ:{o.Category} - 割引なし");
                break;

            case null:
                Console.WriteLine("注文データがありません。");
                break;
        }
    }
}
実行結果
カテゴリ:Electronics - プレミアム特典: 20%割引適用
カテゴリ:Food - 通常特典: 10%割引適用
カテゴリ:Electronics - 通常特典: 10%割引適用

この例では、Order オブジェクトの状態を when 句で詳細にチェックしています。

重要な点は、上から順に評価されるため、より条件が厳しいものを上に配置する 必要があるという点です。

もし2番目のケースを最初に書いてしまうと、プレミアム会員の1万円以上の注文もすべて2番目のケースに吸い込まれてしまいます。

C# 8.0以降のスイッチ式 (switch expression) との併用

現代のC#開発において、値を返すだけの分岐であれば、従来の switch 文よりも 「スイッチ式 (switch expression)」 が好まれます。

スイッチ式でも when 句は同様に使用でき、さらに簡潔な記述が可能です。

C#
using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine(GetBmiStatus(18.0));
        Console.WriteLine(GetBmiStatus(22.5));
        Console.WriteLine(GetBmiStatus(26.0));
    }

    public static string GetBmiStatus(double bmi) => bmi switch
    {
        < 18.5 => "低体重",
        >= 18.5 and < 25.0 when bmi > 22.0 => "普通体重(やや高め)",
        >= 18.5 and < 25.0 => "普通体重",
        _ => "肥満"
    };
}
実行結果
低体重
普通体重(やや高め)
肥満

スイッチ式では case キーワードが不要になり、ラムダ式のような => 記法を用います。

when 句はパターンの直後に記述します。

なお、C# 9.0以降の「リレーショナルパターン(<>)」と組み合わせることで、より柔軟な記述が可能になっています。

上記の例では、and パターンで範囲を指定しつつ、特定のケースに対してのみ when で追加条件(22.0より大きいか)を付与しています。

when句とリレーショナルパターンの使い分け

C# 9.0以降、case < 10 のようなリレーショナルパターンが導入されたため、単純な数値比較であれば when 句を使わなくても書けるようになりました。

しかし、以下のようなケースでは依然として when 句が不可欠です。

外部メソッドの呼び出し

パターン内部ではメソッド呼び出しを直接行えませんが、when句の中であれば、boolを返す任意のメソッドを呼び出して条件に使用できます。

複数プロパティの相関比較

プロパティ同士を相関的に比較する(例: 「プロパティAがプロパティBより大きい」)ような条件は、パターンの構文だけでは表現できないため、when句で比較を行う必要があります。

変数との比較(リレーショナルパターン)

リレーショナルパターン内で比較に使えるのは原則として定数のみです。

しかし、when句では実行時の変数と比較することができます。

C#
// 外部変数やメソッドを利用する例
int limit = 100;
object input = 120;

string result = input switch
{
    int n when n > limit && IsValid(n) => "制限を超えた有効な値",
    int n => "制限内の値",
    _ => "不明"
};

bool IsValid(int val) => val % 2 == 0; // 偶数チェック

このように、when 句は 「パターンの枠を超えた動的なロジック」 を差し込むための強力なツールとなります。

実践的な活用シーンと注意点

when 句を効果的に活用するためのポイントをいくつか挙げます。

例外処理 (catch句) での利用

実は when 句は switch 文以外に、try-catch ブロックでも使用できます。

特定の例外が発生し、かつその例外のプロパティ(エラーコードなど)が特定の条件を満たす場合のみキャッチするという記述が可能です。

C#
try
{
    // 何らかの処理
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
    // 404エラーの場合のみここで処理
}

可読性と順序の重要性

switch 文に when 句を多用すると、条件の評価順序が重要になります。

C#のコンパイラは、when 句のない単純な定数一致であれば最適化を行いますが、when 句がある場合は 上から順番に評価されます

そのため、「最も特殊な(条件が厳しい)ケース」を最初に書き、「一般的なケース」を後に書く のが鉄則です。

この順序を誤ると、意図しない分岐にトラップされるバグの原因となります。

複雑すぎる when 句は避ける

when 句の中にあまりに長い論理式を書くと、逆に可読性が低下します。

その場合は、条件判定ロジックを独立したメソッド(述語)として切り出し、when IsTargetOrder(order) のように記述することで、コードの意図がより明確になります。

まとめ

C#の switch when 句は、単なる値の比較を超え、オブジェクトの状態や実行時の動的な条件に基づいた高度な分岐処理を可能にします。

  • パターンマッチング と組み合わせることで、型判定と値判定を同時に行える。
  • スイッチ式 と併用することで、代入を伴うロジックを非常にコンパクトに記述できる。
  • 複雑な if-else 構造 をフラットに保ち、コードの保守性を高めることができる。
  • 評価順序 に注意し、特殊な条件から記述することが重要。

最新のC#では、パターンマッチングの機能がさらに強化されています。

リレーショナルパターンやロジカルパターン(and, or, not)と when 句を適切に使い分けることで、バグが少なく読みやすい「クリーンコード」を実現しましょう。

この記事が、あなたのC#プログラミングにおける条件分岐のスキルアップに繋がれば幸いです。