Übersicht über Musterabgleiche

Tip

Dieser Artikel ist Teil des Abschnitts "Grundlagen" für Entwickler, die bereits mindestens eine Programmiersprache kennen und C# erlernen. Wenn Sie noch nicht mit der Programmierung vertraut sind, beginnen Sie zuerst mit den Lernprogrammen " Erste Schritte ". Erfahrene C#-Entwickler können die Musterreferenz für die vollständige Syntax und Regeln verwenden.

Kommen Sie aus einer anderen Sprache? C#-Muster spielen eine ähnliche Rolle wie der Musterabgleich in Java und die match-Fälle in Python. C# verwendet Muster in is Ausdrücken, switch Anweisungen und switch Ausdrücken.

Musterabgleich wendet ein Muster auf einen Ausdruck an. Ein Muster ist eine Bedingung zum Testen des Eingabeausdrucks. Die Eingabe für das Muster ist dieser Ausdruck. C# wertet den Eingabeausdruck aus; das Ergebnis ist der ausgewertete Wert. Das Muster prüft, ob dieser Wert einen bestimmten Typ aufweist, gleich einem bestimmten Wert oder einer bestimmten Form ist. Wenn ein Muster übereinstimmt, kann Ihr Code Informationen verwenden, die vom Test gelernt wurden, z. B. einen spezifischeren Typ oder einen aus einem Objekt extrahierten Wert.

Sie können ein Muster in drei Kontexten verwenden:

  • Auf der rechten Seite des Operators is.
  • Im case-Label einer switch-Anweisung.
  • In einem Zweig eines switch-Ausdrucks.

Pattern sind oft klarer als eine Folge von Vergleichsanweisungen, weil jeder Zweig die Daten beschreibt, die er verarbeitet. Die folgende Methode verwendet z. B. einen switch Ausdruck, um eine Zustellungsnachricht auszuwählen:

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

Rekursive Muster verfügen über eigene Eingabeausdrücke. In StandardDelivery { Days: <= 2 } erhält das äußere Muster den Ausdruck delivery. Das rekursive <= 2 Muster empfängt den Days Eigenschaftsausdruck aus dem übereinstimmenden StandardDelivery Objekt.

Der Ausdruck davor switch ist der Eingabeausdruck. Jede Zeile in den geschweiften Klammern ist ein Schalterarm. Das Muster erscheint vor =>, und das Ergebnis erscheint danach. C# wertet den Eingabeausdruck aus und wählt dann in Textreihenfolge den ersten Zweig aus, dessen Muster passt und dessen optionale Bedingung whentrue ist. Der optionale when Schutz ist eine zusätzliche boolesche Bedingung, die nach dem Muster geschrieben wurde. Im vorherigen Beispiel wurden die folgenden Muster gezeigt:

  • null ist ein konstantes Muster. Es prüft, ob der Ausdruck delivery zu null ausgewertet wird.
  • ExpressDelivery express ist ein Deklarationsmuster mit zwei Teilen. ExpressDelivery ist der Typtestteil. Es wird geprüft, ob der ausgewertete Wert ein Objekt ist, das nicht NULL ist und dessen Laufzeittyp mit ExpressDelivery kompatibel ist. express ist die Variablebezeichnung: Sie deklariert eine Variable mit dem Namen express und weist ihr das übereinstimmende ExpressDelivery Objekt zu.
  • StandardDelivery { Days: <= 2 } beginnt mit einem Typtest. StandardDelivery überprüft, ob der ausgewertete Wert ein Nicht-Null-Objekt dieses Typs ist. Die geschweiften Klammern enthalten ein Eigenschaftsmuster. Days benennt die zu prüfende Eigenschaft, sodass der Days Eigenschaftsausdruck zur Eingabe des rekursiven Musters wird. Der <= 2 Teil ist ein relationales Muster, das testet, ob der ausgewertete Wert kleiner oder gleich ist 2.
  • _ (Unterstrich) ist das Verwerfenmuster. Er entspricht jedem ausgewerteten Wert, einschließlich null. Da die vorherigen Arme bereits null, Expresslieferungen und Standardlieferungen, die innerhalb von zwei Tagen eintreffen, verarbeiten, übernimmt dieser letzte Arm alle verbleibenden Werte.

Ein Arm ohne when Schutz ist unbewacht. Alle Arme im ersten Beispiel sind unbewacht. Wenn ein früherer nicht überwachter Arm mit jedem ausgewerteten Wert übereinstimmt, den ein späterer Arm abgleichen könnte, wird der spätere Arm subsumiert. Ein subsumiertes Muster ist ein Muster, bei dem alle möglichen Eingabewerte, die übereinstimmen, bereits mit einem früheren Schalterarm übereinstimmen. Es kann niemals übereinstimmen, sodass der Compiler einen Fehler meldet. Der Verwerfen-Arm (_) muss zuletzt erfolgen, da er mit jedem Eingabeausdruck übereinstimmt. Ein Arm mit Guard subsumiert einen späteren Arm nicht allein aufgrund seines Musters, da die Guard möglicherweise false ist.

Ein vollständiger switch-Ausdruck ist gegeben, wenn seine Zweige jeden möglichen Eingabeausdruck abdecken. Das erste Beispiel ist vollständig, da sein letzter Verwerfungszweig alles abfängt, worauf die früheren Zweige nicht zutreffen. Der Compiler warnt, wenn er einen potenziellen Eingabewert erkennt, den kein Arm behandelt. Der Compiler kann für jede Kombination von Mustern nicht erschöpfend sein, aber mit dieser Diagnose können Sie korrekten Musterabgleichscode schreiben. Detaillierte Übereinstimmungs-, Subsumtions- und Erschöpfendheitsregeln finden Sie in der Musterreferenz.

Testen einer Bedingung mit is

Verwenden Sie den is Operator, wenn Sie einen booleschen Test benötigen. Der folgende Code wertet den delivery Ausdruck aus und wendet das Deklarationsmuster ExpressDelivery expressan. Der Typteil stimmt überein, wenn der ausgewertete Wert nicht null ist und sein Laufzeittyp mit ExpressDelivery kompatibel ist. Wenn das Muster zutrifft, deklariert sein Variablenbezeichner express:

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

Der Compiler verfolgt, ob eine lokale Variable zugewiesen wurde, bevor der Code sie liest. Diese Nachverfolgung wird als eindeutige Zuordnung bezeichnet. Innerhalb des if Blocks kann die Bedingung nur sein true , wenn dem Muster das entsprechende Objekt expresszugewiesen wurde. Der Compiler weiß daher, dass express dort definitiv zugewiesen wurde. Ihr Code kann seine TrackingCode Eigenschaft sicher verwenden. Sie benötigen keine separate Umwandlung. Wenn Sie nur die Typprüfung benötigen und keine Variable deklarieren müssen, verwenden Sie ein Typmuster, beispielsweise delivery is ExpressDelivery.

Verwenden Sie is null oder is not null für Nullprüfungen. Diese Muster rufen keine benutzerdefinierten == Oder != Operatoren auf.

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

Weitere Richtlinien zur Nullsicherheit finden Sie unter nullablen Referenztypen.

Wählen Sie zwischen Anweisung und Ausdruck

Muster funktionieren mit beiden Formen von switch:

  • Verwenden Sie eine switch Anweisung, wenn für jeden Fall eine oder mehrere Anweisungen ausgeführt werden sollen.
  • Verwenden Sie einen switch Ausdruck, wenn jede Übereinstimmung einen Wert erzeugen soll.

Die folgende Switch-Anweisung gibt eine Aktualisierung zum Lieferstatus aus. Der Zweig für Expresszustellung schreibt zwei Nachrichten, sodass sich eine Anweisung natürlich einfügt:

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

Jedes case wendet ein Muster auf den delivery Ausdruck an. Der passende Abschnitt kann eine beliebige Anzahl von Anweisungen ausführen, bevor break die switch-Anweisung beendet. Der default Abschnitt behandelt alle Elemente, die in den früheren Fällen nicht übereinstimmen.

Der folgende Switchausdruck ersetzt eine if / else if Kette, die ein Ergebnis zuweist:

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

Ein Schalterausdruck ist prägnant, da jeder Arm denselben Zweck hat: Erzeugen Sie den zurückgegebenen Wert. Verwenden Sie eine Switch-Anweisung, wenn Verzweigungen Aktionen ausführen, und verwenden Sie einen Switch-Ausdruck, wenn Verzweigungen ein Ergebnis berechnen.

Musterkategorien

C# enthält Muster für allgemeine Arten von Datentests:

Musterkategorie Was es testet
Deklaration, Konstante und var Muster Ein Laufzeittyp, ein bestimmter Konstantenwert oder ein beliebiger Wert, den Sie erfassen möchten
Typmuster Ein Laufzeittyp, ohne eine Variable zu deklarieren
Eigenschaften- und Positionsmuster Eigenschaften, Felder oder dekonstruierte Werte
Relationale und logische Muster Vergleiche und Kombinationen wie and, orund not
Listenmuster Die Werte und die Form einer Liste oder eines Arrays
Verwerfen von Mustern und Verwerfen Alle verbleibenden Werte oder ein Wert, den Der Code absichtlich ignoriert

Die in der Tabelle verknüpften Grundlagenartikel enthalten eine fokussierte Abdeckung der kategorien, die derzeit in diesem Abschnitt dokumentiert sind. Vollständige Syntax und Beispiele für alle Musterkategorien finden Sie in der Musterreferenz.

Siehe auch