Eigenschaften- und Positionsmuster

Tip

Dieser Artikel ist Teil des Abschnitts "Grundlagen" für Entwickler, die bereits mindestens eine Programmiersprache kennen und C# erlernen. Beginnen Sie mit der Übersicht über den Musterabgleich , wenn Muster für Sie neu sind. Vollständige Sprachregeln finden Sie unter Eigenschaftenmuster und Positionsmuster in der Sprachreferenz.

Eigenschaften- und Positionsmuster testen beide Teile eines Werts. Der Unterschied besteht darin, wie sie diese Teile identifizieren:

  • Ein Eigenschaftenmuster benennt die zu testden Eigenschaften oder Felder.
  • Ein Positionsmuster identifiziert Werte anhand ihrer Reihenfolge.

Eine Dekonstruktion macht einen sortierten Satz von Komponentenwerten verfügbar. Ein Tupel verfügt bereits über eine Elementreihenfolge; siehe Deconstruct tuples. Für einen anderen Typ definiert eine Deconstruct Methode , welche Komponentenwerte verfügbar gemacht werden, und deren Reihenfolge.

Vergleichen von Namen und Positionen

Das folgende Eigenschaftenmuster testet zwei benannte Eigenschaften eines Wetterwertes mit Temperaturwerten in Grad Celsius:

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

sealed record WeatherReading(int TemperatureC, int HumidityPercent);

Das folgende Positionsmuster testet einen Signalwert gefolgt von einem booleschen Wert:

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
}

Das Eigenschaftenmuster identifiziert seine Eingaben durch die Namen TemperatureC und HumidityPercent. Der Durchquerungscode erstellt ein Tupel aus den getrennten signal Werten und crossingIsClear Werten. Das Tupelmuster identifiziert diese Werte dann nach Reihenfolge: signal zuerst und crossingIsClear zweitens. Ein Positionsmuster ist gut geeignet, da dieses neu erstellte Tupel nur zwei Werte enthält und ihre Reihenfolge für die Entscheidung über das Überqueren eine klare Bedeutung hat.

Wählen Sie ein Eigenschaftenmuster aus, wenn Elementnamen zum Verständnis des Tests beitragen. Eigenschaftsmuster sind in der Regel für Klassen, Strukturen und Datensätze klarer. Wählen Sie ein Positionsmuster aus, wenn die Reihenfolge den Werten bereits eine offensichtliche Bedeutung gibt. Positionsmuster sind am nützlichsten bei Tupeln, die mehrere verwandte Werte in einem Wert mit einer festen Reihenfolge kombinieren.

Geschachtelte Eingaben in rekursiven Mustern nachverfolgen

Eigenschafts- und Positionsmuster sind rekursive Muster: Sie wenden ein anderes Muster auf jede Eigenschaft, jedes Feld oder jede ausgewählte Position an. Der ausgewählte Wert wird zur Eingabe für dieses geschachtelte Muster.

In IsHotAndHumid ist der reading-Ausdruck die Eingabe für das Eigenschaftsmuster. C# wertet diesen Ausdruck vor dem Abgleich aus. Das Muster ruft zwei Werte aus dem resultierenden Objekt ab:

  • Das relationale Muster > 30 testet den Wert von TemperatureC.
  • Das relationale Muster > 70 testet den Wert von HumidityPercent.

Ein äußerer Typtest ist optional, und rekursive Musterklauseln können leer sein. Das leere Eigenschaftenmuster { } entspricht z. B. jedem nicht null ausgewerteten Wert.

Eigenschafts- und Positionsmuster passen nur auf ausgewertete Werte, die ungleich null sind. Wenn null zur Eingabedomäne gehört, wählen Sie ein rekursives Muster, das zuerst auf einen Wert ungleich NULL prüft:

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

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

Der Eingabeausdruck lautet value. C# wertet sie aus, und das { } Eigenschaftsmuster testet den resultierenden Wert auf Nicht-Null, bevor es zugewiesen nonNullValuewird. Der folgende Switchausdruck kann dann mehrere mögliche Laufzeittypen testen. Ihre DateTime Muster und string Typmuster weisen keine Bezeichnung auf, da die Methode nur jeden Typ identifizieren muss und nicht seinen Wert erfasst.

Wenn rekursive Musterklauseln geschachtelte Muster enthalten, wird jede ausgewählte Eigenschaft, jedes Feld oder jede Position zur Eingabe des geschachtelten Musters.

Sie können einen Typtest vor den geschweiften Klammern hinzufügen, wenn der Eingabeausdruck unterschiedliche Typen erzeugen kann. Sie können auch einen Memberpfad verwenden, um eine geschachtelte Eigenschaft zu testen:

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 ist der Eingabeausdruck. C# wertet es zunächst aus und testet, ob der resultierende Wert ein DateTimeist. Der Date Eigenschaftswert dient dann als Eingabe für den DayOfWeek Elementzugriff. Schließlich wird der DayOfWeek Wert zur Eingabe des logischen Musters, das zwei Konstanten testet. Der Abgleich ist erfolgreich, wenn der umgebende Wert den angegebenen Typ aufweist und jedes Objekt, das entlang des Memberpfads benötigt wird, nicht null ist.

Vergleiche von Mustern mit Verzweigungsanweisungen

Die frühere DescribeDate Methode gibt vier Ergebnisse als Muster aus:

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"
    };

Die folgende Methode erzeugt dieselben Ergebnisse mit einer Reihe imperativer Verzweigungsanweisungen:

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";
}

Die musterbasierte Version hält die möglichen Ergebnisse zusammen, wenn mehrere Zweige den Typ und die Struktur eines Werts prüfen. Die imperative Version macht jeden Test- und Rückgabeschritt explizit. Bei einer Bedingung sieht beide Formulare möglicherweise ähnlich aus. Da die Anzahl verwandter Verzweigungen wächst, können Muster die Alternativen einfacher vergleichen.

Siehe auch