パターン マッチングの概要

Tip

この記事は、少なくとも 1 つのプログラミング言語を既に知っており、C# を学習している開発者向けの 基礎 セクションの一部です。 プログラミングを初めて使用する場合は、最初に「 はじめ に」チュートリアルから始めてください。 経験豊富な C# 開発者は、完全な構文と規則の パターン リファレンス を使用できます。

別の言語から来ていますか? C# パターンは、Javaのパターン マッチングやPythonのmatchケースと同様の役割を果たします。 C# では、 is 式、 switch ステートメント、および switch 式でパターンが使用されます。

パターン マッチング は、式にパターンを適用します。 パターンは、入力式をテストする条件です。 パターン入力はその式です。 C# は入力式を評価します。結果は 評価された値です。 このパターンは、その値が特定の型を持っているか、特定の値と等しいか、特定の図形を持っているかをテストします。 パターンが一致する場合、コードでは、より具体的な型やオブジェクトから抽出された値など、テストによって学習された情報を使用できます。

パターンは、次の 3 つのコンテキストで使用できます。

  • is演算子の右側。
  • switch ステートメントのcase ラベル内。
  • switch式の一部に。

パターンは、多くの場合、各分岐が処理するデータを記述するため、比較ステートメントのシーケンスよりも明確です。 たとえば、次の方法では、 switch 式を使用して配信メッセージを選択します。

static string GetDeliveryMessage(Delivery? delivery) =>
    delivery switch
    {
        null => "No delivery was scheduled.",
        ExpressDelivery express => $"Express package {express.TrackingCode}",
        StandardDelivery { Days: <= 2 } => "Standard delivery arriving soon",
        _ => "Standard delivery"
    };

再帰パターンには、独自の入力式があります。 StandardDelivery { Days: <= 2 }では、外側のパターンはdelivery式を受け取ります。 再帰<= 2 パターンは、一致するStandardDelivery オブジェクトからDaysプロパティ式を受け取ります。

switch前の式は入力式です。 ブレース内の各行は スイッチアームです。 パターンは =>の前に表示され、その後に結果が表示されます。 C# は入力式を評価し、パターンが一致し、オプションの when ガードが trueされている最初の arm をテキスト順に選択します。 オプションの when ガードは、パターンの後に書き込まれた追加のブール条件です。 前の例では、次のパターンが示されています。

  • null定数パターンです。 delivery式がnullに評価されるかどうかをテストします。
  • ExpressDelivery express は、2 つの部分を持つ 宣言パターン です。 ExpressDelivery はタイプ テスト パーツです。 評価された値が、実行時の型が ExpressDeliveryと互換性のある null 以外のオブジェクトであるかどうかをテストします。 express変数の指定です。 express という名前の変数を宣言し、一致する ExpressDelivery オブジェクトを割り当てます。
  • StandardDelivery { Days: <= 2 } は型テストで始まります。 StandardDelivery は、評価された値がその型の null 以外のオブジェクトであるかどうかをテストします。 中かっこには プロパティ パターンが含まれています。 Days は検査するプロパティに名前を付けるので、 Days プロパティ式が再帰パターンへの入力になります。 <= 2部分はリレーショナル パターンであり、評価された値が2以下かどうかをテストします。
  • _ (アンダースコア) は 破棄パターンです。 nullを含め、評価されたすべての値と一致します。 以前のアームは既に 2 日以内に到着する null、高速配送、標準配送を処理しているため、この最後のアームは残りのすべての値を処理します。

whenガードを持たない腕は無防備です。 最初の例のすべての腕は無防備です。 先行するガードのないアームが、後続のアームに一致し得るすべての評価対象の値に一致する場合、その後続のアームは 包含されますサブスム化されたパターンは、一致する可能性のあるすべての入力値が、以前のスイッチ アームによって既に照合されているパターンです。 一致することはできないため、コンパイラはエラーを報告します。 破棄アーム (_) は、すべての入力式に一致するため、最後に来る必要があります。 ガード付きアームは、ガードが false である可能性があるため、そのパターンだけでは後続のアームを包含しません。

スイッチ式は、アームが可能なすべての入力式を処理する場合に 網羅的 です。 最初の例が網羅的であるのは、最後の破棄用のアームが、それ以前のアームに一致しないものをすべて処理するためです。 コンパイラは、どのアームでも処理されない可能性のある入力値を検出すると警告します。 コンパイラは、パターンの組み合わせごとに網羅性を証明することはできませんが、この診断は正しいパターン マッチング コードを記述するのに役立ちます。 詳細な照合、再開、および網羅性の規則については、 パターンのリファレンスを参照してください

で 1 つの条件をテストする is

1 つのブール値テストが必要な場合は、 is 演算子を使用します。 次のコードは、 delivery 式を評価し、宣言パターンを ExpressDelivery express適用します。 型部分は、評価された値が null 以外で、実行時の型が ExpressDeliveryと互換性がある場合に一致します。 パターンが一致すると、その変数の指定で expressが宣言されます。

static void PrintTrackingCode(Delivery delivery)
{
    if (delivery is ExpressDelivery express)
    {
        Console.WriteLine($"Track express package {express.TrackingCode}");
    }
}

コンパイラは、コードが読み取る前にローカル変数が割り当てられているかどうかを追跡します。 この追跡は 、明確な割り当てと呼ばれます。 if ブロック内では、パターンが一致するオブジェクトをexpressに割り当てた場合にのみ、条件をtrueできます。 したがって、コンパイラは、 express が確実にそこに割り当てられていることを認識しています。 コードでは、その TrackingCode プロパティを安全に使用できます。 別のキャストは必要ありません。 型テストのみが必要で、変数を宣言する必要がない場合は、delivery is ExpressDeliveryなどの型パターンを使用します。

null チェックには、 is null または is not null を使用します。 これらのパターンでは、ユーザー定義の ==!= 演算子は呼び出されません。

static void PrintPackageStatus(Delivery? delivery)
{
    if (delivery is null)
    {
        Console.WriteLine("No package is available.");
    }
    else
    {
        Console.WriteLine("A package is ready to track.");
    }
}

null 安全に関する詳細については、Null 許容参照型を参照してください。

ステートメントまたは式を選択する

パターンは、次の両方の形式の switchで動作します。

  • 各一致で 1 つ以上のステートメントを実行する必要がある場合は、switch ステートメントを使用します。
  • 各一致で値を生成する必要がある場合は、 switch 式を使用します。

次の switch ステートメントは、配信の更新を報告します。 高速配信ブランチは 2 つのメッセージを書き込むため、ステートメントは自然に収まります。

static void PrintDeliveryUpdate(Delivery? delivery)
{
    switch (delivery)
    {
        case null:
            Console.WriteLine("No delivery was scheduled.");
            break;
        case ExpressDelivery express:
            Console.WriteLine($"Express delivery {express.TrackingCode} is ready.");
            Console.WriteLine("Notify the priority desk.");
            break;
        case StandardDelivery standard:
            Console.WriteLine($"Standard delivery arrives in {standard.Days} days.");
            break;
        default:
            Console.WriteLine("Another delivery type is scheduled.");
            break;
    }
}

case は、 delivery 式にパターンを適用します。 一致するセクションでは、スイッチを終了 break 前に任意の数のステートメントを実行できます。 defaultセクションは、前のケースと一致しないものを処理します。

次の switch 式は、1 つの結果を割り当てる if / else if チェーンを置き換えます。

static string GetStatusMessage(StandardDelivery delivery) =>
    delivery.Days switch
    {
        0 => "Delivered today",
        1 => "Arriving tomorrow",
        <= 3 => "Arriving soon",
        _ => "In transit"
    };

switch 式は、すべてのアームが同じ目的を持ち、返される値を生成するため、簡潔です。 分岐がアクションを実行するときに switch ステートメントを使用し、分岐が 1 つの結果を計算するときに switch 式を使用します。

パターン カテゴリ

C# には、一般的な種類のデータ テストのパターンが含まれています。

パターン カテゴリ テスト対象
宣言、定数、および var パターン 実行時の型、特定の定数値、またはキャプチャする任意の値
型パターン 変数を宣言しないランタイム型
プロパティと位置パターン プロパティ、フィールド、または分解された値
リレーショナルパターンと論理パターン andor、およびnotなどの比較と組み合わせ
パターン一覧 リストまたは配列の値と図形
破棄パターンと破棄 残りの値、またはコードが意図的に無視する値

この表にリンクされている基礎に関する記事では、このセクションで現在説明されているカテゴリについて重点的に取り上げられています。 すべてのパターン カテゴリの完全な構文と例については、パターンリファレンス を参照してください

こちらも参照ください