宣言、定数、および var パターン

Tip

この記事は、少なくとも 1 つのプログラミング言語を既に知っており、C# を学習している開発者向けの 基礎 セクションの一部です。 C# パターンを使用したことがない場合は、 パターン マッチングの概要 から始めます。 完全な言語規則については、 パターンのリファレンスを参照してください

パターンが入力式に適用されます。 C# は式を評価し、パターンによって結果の値をテストまたはキャプチャします。 宣言、定数、および var パターンは、3 つの実用的な質問に答えます。

  • 宣言パターン: この式では、互換性のあるランタイム型の null 以外の値が生成されましたか? その場合は、その値の変数を宣言します。
  • 定数パターン: 式は特定の定数値を 1 つ生成しましたか?
  • var パターン: 式によって生成された値は何ですか? 最初に型または値をテストせずにキャプチャします。

宣言パターンを使用して型をテストしてキャプチャする

宣言パターンは、型と指定で構成されます。 この型は、テストする実行時の型を指定します。 指定は、一致する値を受け取る変数を宣言します。

次の例では、 objectを受け取るので、式によってさまざまな型が生成される可能性があります。 宣言パターンにより、一致する分岐では、個別の型テストとキャストなしで 10 進数を使用できます。

static void PrintPrice(object value)
{
    if (value is decimal amount)
    {
        Console.WriteLine($"Price: {amount:C}");
    }
}

value is decimal amountの場合:

  • value は入力式です。 C# はそれを最初に評価します。
  • decimal はテストされた型です。 このパターンは、評価された値が null 以外で、実行時の型が decimal と互換性がある場合に一致します。
  • amount は指定です。 パターンが一致すると、 amount が宣言され、10 進値が割り当てられます。

コンパイラは、コードが値を読み取る前にローカル変数が値を受け取るかどうかを追跡します。 この追跡は 、明確な割り当てと呼ばれます。 if ブロック内では、パターンが一致した場合にのみブロックが実行されるため、コンパイラはamountが割り当てられたことを認識します。 コードが if ブロック外のamountにアクセスしようとすると、コンパイラによってエラーが生成されます。 valuedecimal値でない場合、変数amountは値に割り当てられません。

一致する分岐で結果をテストされた型として使用する必要がある場合は、宣言パターンを選択します。 テスト、変換、変数の宣言が組み合わされているため、式の繰り返しや個別のキャストの記述を回避できます。

1 つの式でいくつかの便利な型が生成される可能性がある場合は、宣言パターンを使用することもできます。

static string FormatSensorValue(object reading) =>
    reading switch
    {
        int count => $"Count: {count}",
        double temperature => $"Temperature: {temperature:F1}°C",
        string message => $"Message: {message}",
        _ => "Unsupported reading"
    };

結果にはその型の書式設定動作が必要であるため、各 arm は一致する型の変数を宣言します。 宣言パターンは、評価された値が null 以外で、パターンに対して許可される変換によってテストされた型と互換性のあるランタイム型を既に持っている場合にのみ一致します。 null には、パターンが一致するランタイム型がありません。 このパターンでは、ユーザー定義の変換演算子も実行されません。これは型テストとキャプチャであり、値を別の型に変換する要求ではありません。 完全な互換性規則については、「 宣言と型パターン」を参照してください。

特定の値を定数パターンと一致させる

定数パターンは、式が特定の定数 (数値、文字列、ブール値、列挙型メンバー、宣言されたconst値、nullなど) を生成するかどうかをテストします。

いくつかの既知の値がそれぞれ異なる結果を生成する場合、定数パターンは switch 式に適合します。

static string GetCommandMessage(Command command) =>
    command switch
    {
        Command.Start => "Starting",
        Command.Stop => "Stopping",
        Command.Pause => "Pausing",
        _ => "Unknown command"
    };

Command は列挙型であり、名前付き定数のセットを定義する型です。 Command.StartCommand.Stop、および Command.Pause はその列挙型メンバーであるため、各スイッチ アームは定数パターンを使用して、1 つの名前付きコマンド値をテストします。

コマンドが複数の個別の意味を持つ場合は、この形式を選択します。 スイッチ アームは値とその結果を一緒に保持します。 1 つの単純な等値比較では、通常、if (command == Command.Start)などのifステートメントを読みやすくなります。

定数パターン マッチングでは、ユーザー定義の == 演算子ではなく、組み込みの言語等価ルールが使用されます。 等価性と変換の詳細な規則については、 定数パターンのリファレンスを参照してください

null定数パターンは、信頼性の高い null チェックに役立ちます。

static bool HasText(string? text) => text is not null;

null 状態をチェックする場合は、 is null または is not null を選択します。 これらのパターンでは、式の型オーバーロードが ==場合でも、ユーザー定義の等値演算子は呼び出されません。

var パターンを使用してガードの結果をキャプチャする

var パターンは、nullを含むすべての結果に一致し、型が入力式のコンパイル時型である変数を宣言します。 別のパターンが既にオブジェクトと一致している間に、計算された値をキャプチャできます。

static string GetDeliveryMessage(object delivery) =>
    delivery switch
    {
        ExpressDelivery express
            when EstimateDays(express) is var days && days <= 2
                => $"Arrives in {days} day{(days == 1 ? "" : "s")}",
        ExpressDelivery => "Express delivery for your location takes more than two days",
        _ => "Standard delivery"
    };

static int EstimateDays(ExpressDelivery delivery) =>
    delivery.MilesAway <= 500 ? 1 :
    delivery.MilesAway <= 1_000 ? 2 : 3;

record ExpressDelivery(int MilesAway);

宣言パターン ExpressDelivery express は、最初に配信オブジェクトを expressとしてキャプチャします。 メソッド呼び出し EstimateDays(express) は、 var パターンの入力式です。 C# はそのメソッド呼び出しを評価し、 var days は、その型または値をテストせずに、結果の推定を days としてキャプチャします。 ガードが成功した場合、見積もりは1日または2日になることがあります。 arm の結果には、実際の日数を報告するためにキャプチャされた値が必要です。

通常のローカル変数は、スイッチアーム パターンとその when ガードの間で宣言できません。 結果で EstimateDays(express) を再度呼び出すと、計算が繰り返されます。 コードが既に一致していて、ガードと結果の両方に計算された中間値が必要な場合は、この var パターン フォームを選択します。

キャプチャされた値が不要な場合は、変数を宣言する代わりに 破棄パターン _ を使用します。

こちらも参照ください