プロパティと位置パターン

Tip

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

プロパティ パターンと位置パターンは、値の両方のテスト 部分です。 違いは、これらの部分を識別する方法です。

  • プロパティ パターンは、テストするプロパティまたはフィールドに名前を付けます。
  • 位置指定パターンは、その順序で値を識別します。

分解では、順序付けられたコンポーネント値のセットが公開されます。 タプルには既に要素の順序があります。「 タプルの分解」を参照してください。 別の型の場合、Deconstruct メソッドは、公開されるコンポーネント値とその順序を定義します。

名前と位置を比較する

次のプロパティ パターンは、気温の値が摂氏で表される、天気予報の 2 つの名前付きプロパティをテストします。

static bool IsHotAndHumid(WeatherReading reading) =>
    reading is { TemperatureC: > 30, HumidityPercent: > 70 };

sealed record WeatherReading(int TemperatureC, int HumidityPercent);

次の位置パターンは、シグナル値に続いてブール値が現れるかどうかを検証します。

static string GetCrossingInstruction(
    PedestrianSignal signal, bool crossingIsClear) =>
    (signal, crossingIsClear) switch
    {
        (PedestrianSignal.Walk, true) => "Cross now",
        (PedestrianSignal.Walk, false) => "Wait for the crossing to clear",
        _ => "Wait for the walk signal"
    };

enum PedestrianSignal
{
    Stop,
    Walk
}

プロパティ パターンは、 TemperatureC 名と HumidityPercent名によって入力を識別します。 クロッシング コードは、別々の signal 値と crossingIsClear 値からタプルを作成します。 タプル パターンは、これらの値を順に識別します。最初の signal と 2 番目 crossingIsClear 。 位置指定パターンは、この新しく作成されたタプルには 2 つの値しかなく、その順序が交差するかどうかの判定において明確な意味を持つため、非常によく適しています。

メンバー名がテストの説明に役立つ場合は、プロパティ パターンを選択します。 通常、プロパティ パターンは、クラス、構造体、およびレコードに対してより明確になります。 順序によって値に明確な意味が与えられている場合は、位置指定パターンを選択します。 位置指定パターンは、複数の関連する値を一定の順序で 1 つの値に結合するタプルで最も便利です。

再帰パターン内の入れ子になった入力項目をたどる

プロパティ パターンと位置パターンは 再帰的なパターンです。選択した各プロパティ、フィールド、または位置に別のパターンが適用されます。 選択した値がその入れ子になったパターンへの入力になります。

IsHotAndHumidでは、reading式はプロパティ パターンへの入力です。 C# は、一致する前にその式を評価します。 パターンは、結果のオブジェクトから 2 つの値を取得します。

  • リレーショナル パターン> 30TemperatureCの値をテストします。
  • リレーショナル パターン> 70HumidityPercentの値をテストします。

外部型テストは省略可能であり、再帰パターン句は空にすることができます。 たとえば、空のプロパティ パターン { } は、null 以外の評価値と一致します。

プロパティと位置指定パターンは、null 以外の評価値にのみ一致します。 nullが入力ドメインの一部である場合は、最初に null 以外の値をチェックする再帰パターンを選択します。

static string DescribeNullableInput(object? value)
{
    if (value is not { } nonNullValue)
    {
        return "No value";
    }

    return nonNullValue switch
    {
        DateTime => "Date",
        string => "Text",
        _ => "Another type"
    };
}

入力式が value。 C# はそれを評価し、 { } プロパティ パターンは、結果の値を null 以外に対してテストしてから、 nonNullValueに割り当てます。 次の switch 式では、使用可能な複数のランタイム型をテストできます。 メソッドは各型を識別するだけで、値をキャプチャする必要がないため、その DateTime および string 型パターンには指定がありません。

再帰パターン句に入れ子になったパターンが含まれている場合、選択した各プロパティ、フィールド、または位置が、入れ子になったパターンへの入力になります。

入力式が異なる型を生成できる場合は、中かっこの前に型テストを追加できます。 メンバー パスを使用して、入れ子になったプロパティをテストすることもできます。

static string DescribeDate(object? value) =>
    value switch
    {
        DateTime { Date.DayOfWeek:
            DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend date",
        DateTime => "Weekday date",
        null => "No date",
        _ => "Not a date"
    };

value は入力式です。 C# は最初にそれを評価し、結果の値が DateTimeかどうかをテストします。 Date プロパティの値は、DayOfWeek メンバー アクセスの入力になります。 最後に、 DayOfWeek 値は、2 つの定数をテストする論理パターンへの入力になります。 外側の値が指定された型を持ち、メンバー パスに沿って必要なすべてのオブジェクトが null 以外の場合、照合は成功します。

パターンと分岐ステートメントを比較する

前の DescribeDate メソッドでは、4 つの結果がパターンとして表されます。

static string DescribeDate(object? value) =>
    value switch
    {
        DateTime { Date.DayOfWeek:
            DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend date",
        DateTime => "Weekday date",
        null => "No date",
        _ => "Not a date"
    };

次のメソッドは、命令型分岐ステートメントのシリーズで同じ結果を生成します。

static string DescribeDateWithBranches(object? value)
{
    if (value is DateTime date)
    {
        if (date.DayOfWeek == DayOfWeek.Saturday ||
            date.DayOfWeek == DayOfWeek.Sunday)
        {
            return "Weekend date";
        }

        return "Weekday date";
    }

    if (value is null)
    {
        return "No date";
    }

    return "Not a date";
}

パターンベースのバージョンでは、複数の分岐で値の型と形状がテストされたときに、可能な結果が一緒に保持されます。 命令型バージョンでは、各テストと戻りステップが明示的になります。 1 つの条件では、どちらのフォームも似ている可能性があります。 関連する分岐の数が増えるにつれて、パターンによって代替手段を比較しやすくなります。

こちらも参照ください