11 Muster und Musterabgleich

11.1 Allgemein

Ein Muster kann mit dem is Operator (§12.15.12) in einem switch_statement (§13.8.3) und in einem switch_expression (§12.12) verwendet werden, um die Form der Daten zu beschreiben, mit denen eingehende Daten verglichen werden sollen. Muster können geschachtelt werden, wobei Teile der Daten mit Untermustern abgeglichen werden.

Ein Muster wird anhand eines Werts in einer Reihe von Kontexten getestet:

  • In einem switch_statement wird das Muster eines switch_label anhand der selector_expression des switch_statement getestet.
  • Bei einem Is-Pattern-Operator wird das Muster auf der rechten Seite mit dem Ausdruck auf der linken Seite getestet.
  • In einem switch_expression wird das Muster eines switch_expression_arm anhand des Ausdrucks auf der linken Seite des switch_expression getestet.
  • In geschachtelten Kontexten wird das Untermuster abhängig vom Musterformular anhand von Werten getestet, die aus Eigenschaften, Feldern oder aus anderen Eingabewerten indiziert wurden.

Der Wert, für den ein Muster getestet wird, wird als Mustereingabewert bezeichnet.

11.2 Musterformulare

11.2.1 Allgemein

Ein Muster kann eine der folgenden Formen aufweisen:

pattern
    : logical_pattern
    ;

primary_pattern
    : parenthesized_pattern
    | declaration_pattern
    | constant_pattern
    | var_pattern
    | positional_pattern
    | property_pattern
    | discard_pattern
    | type_pattern
    | relational_pattern
    | logical_pattern
    | list_pattern
    | slice_pattern
    ;

Die '(' pattern ')' Produktion ermöglicht es, ein Muster in Klammern einzuschließen, um die Reihenfolge der Auswertung zwischen Mustern unter Verwendung eines der logical_patterns zu erzwingen.

Kann die Eingabe syntaktisch sowohl als constant_pattern als auch als positional_pattern erkannt werden, so wird die constant_pattern gewählt.

Einige Musterkönnen zur Deklaration einer lokalen Variablen führen.

Jedes Musterformular definiert den Satz von Typen für Eingabewerte, auf die das Muster angewendet werden kann. Ein Muster P gilt für einen Typ T , wenn T es sich um die Typen handelt, deren Werte das Muster möglicherweise abgleichen kann. Es handelt sich um einen Kompilierungsfehler, wenn ein Muster P in einem Programm angezeigt wird, um einem Mustereingabewert (§11.1) des Typs T zu entsprechen, falls P nicht zutreffend T.

Beispiel: Im folgenden Beispiel wird ein Kompilierungszeitfehler generiert, da der Kompilierungszeittyp v lautet TextReader. Eine Variable vom Typ TextReader kann niemals einen Wert aufweisen, der referenzkompatibel ist mit string:

TextReader v = Console.In; // compile-time type of 'v' is 'TextReader'
if (v is string) // compile-time error
{
    // code assuming v is a string
}

Im Folgenden wird jedoch kein Kompilierungszeitfehler generiert, da der Kompilierungszeittyp v lautet object. Eine Variable des Typs object könnte einen Wert aufweisen, der referenzkompatibel ist mit string:

object v = Console.In;
if (v is string s)
{
    // code assuming v is a string
}

Endbeispiel

Jedes Musterformular definiert den Wertesatz, für den das Muster dem Wert zur Laufzeit entspricht .

Die Reihenfolge der Auswertung von Vorgängen und Nebenwirkungen während des Musterabgleichs (Aufrufe von Deconstruct, Eigenschaftenzugriffen und Aufrufe von Membern von System.Runtime.CompilerServices.ITuple) wird nicht angegeben.

11.2.2 Deklarationsmuster

Ein declaration_pattern wird verwendet, um zu testen, ob ein Wert einen bestimmten Typ aufweist, und wenn der Test erfolgreich ist, um optional den Wert in einer Variablen dieses Typs bereitzustellen.

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : discard_designation
    | single_variable_designation
    ;
discard_designation
    : '_'
    ;
single_variable_designation
    : identifier
    ;

Bei der Anerkennung einer simple_designation , wenn sowohl die discard_designation als auch die single_variable_designation Alternativen anwendbar sind, wird der erste Ausgewählt.

Hinweis: ANTLR macht die angegebene Auswahl aufgrund der Reihenfolge der Alternativen von simple_designation automatisch. Endnote

Der Laufzeittyp des Werts wird anhand der gleichen Regeln getestet, die im Is-Type-Operator (§12.15.12.12.1) angegeben sind. Wenn der Test erfolgreich ist, entspricht das Muster diesem Wert. Es handelt sich um einen Kompilierungszeitfehler, wenn der Typ ein Nullwerttyp (§8.3.12) oder ein nullabler Bezugstyp (§8.9.3) ist. Dieses Musterformular entspricht niemals einem null Wert.

Hinweis: Der Is-Type-Ausdruck e is T und das Deklarationsmuster e is T _ sind gleichwertig, wenn T es sich nicht um einen Null-Typ handelt. Endnote

Bei einem Mustereingabewert (§11.1) e, wenn die simple_designation ein discard_designation ist und ein Verwerfen (§9.2.9.2) angibt, ist der Wert von e nicht an nichts gebunden. Andernfalls wird eine lokale Variable (§9.2.9) des angegebenen Typs eingeführt, wenn die simple_designation ein single_variable_designation ist. Diese lokale Variable wird dem Wert des Mustereingabewerts zugewiesen, wenn das Muster dem Wert entspricht .

Hinweis: Diese Behandlung _ innerhalb einer declaration_pattern unterscheidet sich von der eines eigenständigen _geschriebenen Musters (§11.2.7): In letzterem Fall ist eine In-Scope-Konstante oder ein Typ namens _, falls vorhanden, nicht ausgeblendet. Endnote

Ein Typ E wird als Muster angegeben , das mit dem Typ T kompatibel ist, wenn eine Identitätskonvertierung, eine implizite oder explizite Verweiskonvertierung, eine Boxkonvertierung, eine Unboxing-Konvertierung oder eine implizite oder explizite Nullwerttypkonvertierung von E zu T, oder wenn ET es sich um einen offenen Typ (§8.4.3) handelt. Ein Deklarationsmuster, das einen Typ T benennt, gilt für (§11.2.1) jeden Typ E , für den E das Muster kompatibel Tist. Es handelt sich um einen Kompilierungsfehler, wenn ein Deklarationsmuster verwendet wird, das einen Typ T benennt, um einem Mustereingabewert (§11.1) zu entsprechen, dessen statischer Typ E nicht mit Tdem Muster kompatibel ist.

Hinweis: Die Unterstützung für offene Typen kann am nützlichsten sein, wenn Typen überprüft werden, die entweder Struktur- oder Klassentypen sein können, und boxen ist zu vermeiden. Endnote

Beispiel: Das Deklarationsmuster ist nützlich, um Laufzeittypentests von Referenztypen durchzuführen und das Idiom zu ersetzen.

var v = expr as Type;
if (v != null) { /* code using v */ }

mit der etwas prägnanteren

if (expr is Type v) { /* code using v */ }

Endbeispiel

Beispiel: Das Deklarationsmuster kann verwendet werden, um Werte von nullablen Typen zu testen: Ein Wert vom Typ Nullable<T> (oder ein Boxed T) entspricht einem Typmuster T2 id , wenn der Wert ungleich NULL ist und T2Toder ein Basistyp oder eine Schnittstelle von T. Beispiel: im Codefragment

int? x = 3;
if (x is int v) { /* code using v */ }

Die Bedingung der if Anweisung befindet true sich zur Laufzeit, und die Variable v enthält den Wert 3 des Typs int innerhalb des Blocks. Nach dem Block befindet sich die Variable v im Bereich, aber nicht definitiv zugewiesen. Endbeispiel

11.2.3 Konstantenmuster

Ein constant_pattern wird verwendet, um den Wert eines Mustereingabewerts (§11.1) anhand des angegebenen Konstantenwerts zu testen.

constant_pattern
    : constant_expression
    ;

Ein Konstantenmuster P gilt für einen Typ T , wenn eine implizite Konvertierung vom konstanten Ausdruck in P den Typ Tvorhanden ist.

Bei einem Konstantenmuster Pist der konvertierte Wert

  • wenn der Typ des Mustereingabewerts ein integraler Typ oder ein Enumerationstyp ist, wird der Konstantenwert des Musters in diesen Typ konvertiert; sonst
  • wenn der Typ des Mustereingabewerts die NULL-Version eines integralen Typs oder eines Enumerationstyps ist, wird der konstanten Wert des Musters in den zugrunde liegenden Typ konvertiert; sonst
  • der Wert des Konstantenwerts des Musters.

Bei einem Mustereingabewert e und einem konstanten Muster P mit konvertiertem Wert v

  • wenn e einen integralen Typ oder Enumerationstyp hat oder eine nullable Form eines dieser Typen aufweist und v einen integralen Typ aufweist, entsprichtP dem Wert e, wenn das Ergebnis des Ausdrucks e == v lautettrue; andernfalls
  • wenn e vom Typ System.Span<char> oder System.ReadOnlySpan<char>, und v ist eine konstante Zeichenfolge, und v hat keinen konstanten Wert von null, dann wird das Muster als übereinstimmend betrachtet, wenn System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(v)) zurückgegeben true; andernfalls wird das Muster als übereinstimmend betrachtet; andernfalls
  • das Muster Pentspricht dem Wert e , wenn object.Equals(e, v) zurückgegeben wird true.

Beispiel: Die switch Anweisung in der folgenden Methode verwendet fünf Konstantenmuster in ihren Fallbeschriftungen.

static decimal GetGroupTicketPrice(int visitorCount)
{
    switch (visitorCount) 
    {
        case 1: return 12.0m;
        case 2: return 20.0m;
        case 3: return 27.0m;
        case 4: return 32.0m;
        case 0: return 0.0m;
        default: throw new ArgumentException(...);
    }
}

Endbeispiel

11.2.4 Var-Muster

Ein var_patternentspricht jedem Wert. Das heißt, ein Musterabgleichsvorgang mit einem var_pattern ist immer erfolgreich.

Ein var_pattern gilt für jeden Typ.

var_pattern
    : 'var' designation
    ;
designation
    : simple_designation
    | tuple_designation
    ;
tuple_designation
    : '(' designations? ')'
    ;
designations
    : designation (',' designation)*
    ;

Bei einem Mustereingabewert (§11.1) e, wenn die Bezeichnungdiscard_designation ist, wird ein Verwerfen (§9.2.9.2) bezeichnet, und der Wert von e ist an nichts gebunden. (Obwohl eine deklarierte Variable mit diesem Namen zu diesem Zeitpunkt im Bereich enthalten sein kann, wird diese benannte Variable in diesem Kontext nicht angezeigt.) Andernfalls ist die Bezeichnungsingle_variable_designation, zur Laufzeit ist der Wert von e an eine neu eingeführte lokale Variable (§9.2.9) dieses Namens gebunden, deren Typ der statische Typ von e ist, und der Mustereingabewert dieser lokalen Variablen zugewiesen wird.

Es ist ein Fehler, wenn der Name var an einen Typ gebunden würde, in dem ein var_pattern verwendet wird.

Wenn die Bezeichnung eine tuple_designation ist, entspricht das Muster einem positional_pattern (§11.2.5) der (var, ... ) sind die Bezeichnungeninnerhalb der tuple_designation. Das Muster var (x, (y, z)) entspricht (var x, (var y, var z))z. B. .

11.2.5 Positionsmuster

Ein positional_pattern überprüft, ob der Eingabewert nicht nullist, extrahiert eine Abfolge von Werten daraus und gleicht jeden extrahierten Wert mit einem entsprechenden Unterpattern ab. Die Werte werden auf eine von drei Arten extrahiert: durch Behandeln der Eingabe als Tupel, durch Aufrufen einer Deconstruct Methode oder durch Indizieren der Eingabe durch System.Runtime.CompilerServices.ITuple.

Hinweis: Die Verwendung hier Deconstruct unterscheidet sich von der in §12.7 definierten Destrukturierungstransformation auf Quellebene. Endnote

positional_pattern
    : type? '(' subpatterns? ')' property_subpattern? simple_designation?
    ;
subpatterns
    : subpattern (',' subpattern)*
    ;
subpattern
    : pattern
    | subpattern_name ':' pattern
    ;
subpattern_name
    : identifier
    | subpattern_name '.' identifier
    ;

Lassen Sie n die Anzahl der Unterpatternsein, die zwischen den Klammern angezeigt werden. Die Abgleichsstrategie wird zur Kompilierungszeit ausgewählt, indem die folgenden Fälle in der angegebenen Reihenfolge angewendet werden. der erste Fall, dessen Bedingungen erfüllt sind, wird verwendet, und die übrigen Fälle werden nicht berücksichtigt. Sobald ein Fall ausgewählt ist, wird diese Strategie übernommen: Jeder in diesem Fall angegebene Kompilierungszeitfehler wird gemeldet, und der Abgleich fällt nicht in einen nachfolgenden Fall.

  1. Tupelformular. Wenn der Typ ausgelassen wird und der statische Typ des Eingabewerts ein Tupeltyp (§8.3.11) ist oder der Eingabewert ein Tupelliteral (§12.8.6) ist, gilt dieser Fall. Es handelt sich um einen Kompilierungsfehler, wenn n nicht der Arität dieses Tupeltyps entspricht. Zur Laufzeit wird jedes Tupelelement mit dem entsprechenden Unterpattern abgeglichen; die Übereinstimmung erfolgreich ist, wenn alle diese erfolgreich sind. Wenn ein Unterpattern über einen Bezeichner verfügt, muss dieser Bezeichner das Tupelelement an der entsprechenden Position im Tupeltyp benennen.
  2. Deconstruct form. Andernfalls gilt, wenn der Typ vorhanden ist oder der Typ ausgelassen wird und der statische Typ des Eingabewerts eine barrierefreie Deconstruct Methode (§12.7) enthält. Geben Sie Dein , wenn der Typ vorhanden ist; andernfalls ist D der statische Typ des Eingabewerts. Eine Deconstruct Methode wird aus D unter Verwendung der gleichen Überladungsauflösungsregeln wie für eine Dekonstruktionsdeklaration ausgewählt, wobei die zusätzliche Anforderung, dass die Anzahl der out Parameter gleich n ist; es handelt sich um einen Kompilierungszeitfehler, wenn keine solche Methode vorhanden ist. Wenn der Typ vorhanden ist, handelt es sich um einen Kompilierungszeitfehler, wenn der statische Typ des Eingabewerts nicht musterkompatibel (§11.2.2) mit Typ ist; zur Laufzeit wird der Eingabewert anhand des Typs getestet, und wenn dieser Test fehlschlägt, schlägt die Positionsmustervergleich fehl. Andernfalls wird der Eingabewert in D konvertiert, und die ausgewählte Deconstruct Methode wird mit neuen Variablen aufgerufen, die ihre out Parameter erhalten. Jeder empfangene Wert wird mit dem entsprechenden Unterpattern abgeglichen, und die Übereinstimmung ist erfolgreich, wenn alle diese erfolgreich sind. Wenn ein Unterpattern über einen Bezeichner verfügt, muss dieser Bezeichner den Parameter an der entsprechenden Position von Deconstruct.
  3. ITuple-Formular. Andernfalls weist keinTyp einen Bezeichner auf, und der statische Typ des Eingabewerts lautet object, System.Runtime.CompilerServices.ITupleoder ein Typ, der eine implizite Verweiskonvertierung System.Runtime.CompilerServices.ITupleenthält , und dieser Fall gilt. Zur Laufzeit wird der Eingabewert als eine Nichtinstanznull von System.Runtime.CompilerServices.ITuple; wenn dieser Test fehlschlägt, schlägt die Positionsmustervergleich fehl. Andernfalls wird die Eigenschaft des Werts Length gelesen, und wenn sie nicht gleich n ist, schlägt die Positionsmustervergleich fehl. Andernfalls wird für jeden i von 1 bis n der Durch indizierung des Eingabewerts mit i − 1 mit dem i-th-Unterpattern abgeglichen, und die Übereinstimmung ist erfolgreich, wenn alle diese Ergebnisse erfolgreich sind.
  4. Andernfalls gilt kein Fall, und die positional_pattern ist ein Kompilierungszeitfehler.

Die Reihenfolge, in der Unterpattern zur Laufzeit abgeglichen werden, ist nicht angegeben, und eine fehlgeschlagene Übereinstimmung versucht möglicherweise nicht, alle Unterpattern abzugleichen.

Beispiel: Hier deconieren wir ein Ausdrucksergebnis und stimmen mit den resultierenden Werten mit den entsprechenden geschachtelten Mustern überein:

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) => (X, Y) = (x, y);
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

Endbeispiel

Beispiel: Die Namen von Tupelelementen und Deconstruct-Parametern können wie folgt in einem Positionsmuster verwendet werden:

var numbers = new List<int> { 10, 20, 30 };
if (SumAndCount(numbers) is (Sum: var sum, Count: var count))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");
}

static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return (sum, count);
}

Die erzeugte Ausgabe ist

Sum of [10 20 30] is 60

Endbeispiel

11.2.6 Eigenschaftenmuster

Ein property_pattern überprüft, ob der Eingabewert nicht nullist, und stimmt rekursiv mit Werten überein, die durch die Verwendung von barrierefreien Eigenschaften oder Feldern extrahiert wurden.

property_pattern
    : type? property_subpattern simple_designation?
    ;
property_subpattern
    : '{' '}'
    | '{' subpatterns ','? '}'
    ;

Es ist ein Fehler, wenn ein Unterpattern eines property_pattern keine subpattern_name enthält.

Es handelt sich um einen Kompilierungszeitfehler, wenn der Typ ein Nullwerttyp (§8.3.12) oder ein nullabler Bezugstyp (§8.9.3) ist.

Hinweis: Ein Nullüberprüfungsmuster fällt aus einem trivialen Eigenschaftsmuster heraus. Um zu überprüfen, ob die Zeichenfolge s ungleich NULL ist, kann eine der folgenden Formulare geschrieben werden:

#nullable enable
string s = "abc";
if (s is object o) ...  // o is of type object
if (s is string x1) ... // x1 is of type string
if (s is {} x2) ...     // x2 is of type string
if (s is {}) ...

Endnote

Bei einer Übereinstimmung eines Ausdrucks e mit denUnterpattern des Mustertyps{} handelt es sich um einen Kompilierungszeitfehler, wenn der Ausdruck e nicht musterkompatibel (§11.2.2) mit dem Typ T ist, der nach Typ bestimmt ist. Wenn der Typ nicht vorhanden ist, wird der Typ als statischer Typ von e angenommen. Jede subpattern_name , die auf der linken Seite ihrer Unterpattern angezeigt wird, muss eine lesbare Eigenschaft oder ein lesbares Feld von T festlegen. Wenn die simple_designation des property_pattern vorhanden ist, deklariert sie eine Mustervariable vom Typ T.

Zur Laufzeit wird der Ausdruck auf T getestet. Wenn dies fehlschlägt, schlägt die Übereinstimmung des Eigenschaftenmusters fehl, und das Ergebnis lautet false. Wenn dies erfolgreich ist, wird jedes property_subpattern Feld oder jede Eigenschaft gelesen und ihr Wert mit dem entsprechenden Muster abgeglichen. Das Ergebnis der gesamten Übereinstimmung ist false nur, wenn das Ergebnis einer dieser Übereinstimmungen lautet false. Die Reihenfolge, in der Unterpattern abgeglichen werden, wird nicht angegeben, und eine fehlgeschlagene Übereinstimmung testet möglicherweise nicht alle Unterpattern zur Laufzeit. Wenn die Übereinstimmung erfolgreich ist und die simple_designation der property_pattern ein single_variable_designation ist, wird der deklarierte Wert zugewiesen.

Die property_pattern können verwendet werden, um eine Mustervergleichung mit anonymen Typen zu erzielen.

Ein subpattern_name kann auf ein geschachteltes Element verweisen. In einem solchen Fall ist der Empfänger für jede Namenssuche der Typ des vorherigen Mitglieds T₀, beginnend mit dem Eingabetyp der property_pattern. Wenn T ein nullabler Typ ist, ist T₀ der zugrunde liegende Typ, andernfalls ist T₀ gleich T. Beispielsweise entspricht ein Muster des Formulars { Prop1.Prop2: pattern } genau dem { Prop1: { Prop2: pattern } }.

Hinweis: Dies schließt die NULL-Prüfung ein, wenn T ein Nullwerttyp oder ein Bezugstyp ist. Diese NULL-Überprüfung bedeutet, dass die verfügbaren geschachtelten Eigenschaften die Eigenschaften von T₀ und nicht von T sind. Da wiederholte Memberpfade zulässig sind, kann die Kompilierung des Musterabgleichs gemeinsame Teile von Mustern nutzen. Endnote

Beispiel:

var o = ...;
if (o is string { Length: 5 } s) ...

Endbeispiel

Beispiel: Eine Laufzeittypüberprüfung und eine Variabledeklaration können einem Eigenschaftenmuster wie folgt hinzugefügt werden:

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));            // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,
    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),
    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

Die erzeugte Ausgabe ist

Hello
Hi!
12345
abc

Endbeispiel

11.2.7 Muster verwerfen

Jeder Ausdruck stimmt mit dem Verwerfenmuster überein, was zu dem Wert des Ausdrucks führt, der verworfen wird.

discard_pattern
    : '_'
    ;

Wenn der syntaktische Kontext ein Muster zulässt, wird das Token _ als simple_name (§12.8.4) zu einer barrierefreien Konstante oder einem Typ aufgelöst, dann _nicht als discard_pattern behandelt. Stattdessen:

  • Wenn _ eine Barrierefreiheitskonstante aufgelöst wird, wird sie _ als constant_pattern (§11.2.3) interpretiert, dessen Konstantenausdruck diese Konstante ist.
  • Wenn _ er in einen Typ aufgelöst wird, wird das Konstrukt auf der rechten Seite eines is Operators relational_expressionis _ als is-type-Operator (§12.15.12.1) interpretiert, der anhand dieses Typs getestet wird. In einem anderen syntaktischen Kontext, der ein Muster zulässt, ist die auflösungslose _ Auflösung eines Typs nicht allein ein gültiges Muster; kann jedoch als Typ eines declaration_pattern (z. B. ) oder in anderen Musterformen angezeigt werden, _ x_ die einen Typ explizit benennen.

Diese Regel behält die Abwärtskompatibilität mit Code bei, der vor der Einführung des Verwerfensmusters als Typ oder Bezeichner definiert _ wurde. Wenn _ ein anderer Wert als eine barrierefreie Konstante oder ein beliebiger Typ (z. B. eine lokale Variable, ein Parameter, ein Feld oder eine Methode) aufgelöst wird, gilt die Regel nicht und _ bleibt ein discard_pattern.

Hinweis: Dies entspricht der Regel in var§11.2.4, mit der Ausnahme, dass eine _ In-Scope-Konstante oder ein Typ _ als Verweis auf diese Deklaration interpretiert wird, anstatt einen Fehler zu erzeugen. Endnote

Wenn das Token _ nach dem Anwenden der vorhergehenden Regel immer noch ein discard_pattern ist, handelt es sich um einen Kompilierungszeitfehler, damit dieser discard_pattern als gesamtes Muster eines relational_expression des Formulars relational_expressionisMusters oder als gesamtes Muster eines switch_label angezeigt wird. Ein discard_pattern kann jedoch als Unterpattern eines umschließenden Musters (z. B. als Unterpattern einer positional_pattern oder property_pattern) angezeigt werden.

Hinweis: Verwenden Sie in diesen Fällen eine var_pattern mit einem Verwerfen var _, um einem beliebigen Ausdruck zu entsprechen. Endnote

Beispiel:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));
Console.WriteLine(GetDiscountInPercent(null));
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

Die erzeugte Ausgabe ist

5.0
0.0
0.0

Hier wird ein Verwerfenmuster verwendet, um einen ganzzahligen Wert zu behandeln null , der nicht über das entsprechende Element der DayOfWeek Enumeration verfügt. Dadurch wird sichergestellt, dass der switch Ausdruck alle möglichen Eingabewerte verarbeitet. Endbeispiel

Beispiel: Im Folgenden wird veranschaulicht, wie eine In-Scope-Konstante mit dem Namen _ die Interpretation eines _ Arms in einem switch Ausdruck ändert. In WithoutUnderscore, ist der _ Arm ein discard_pattern und entspricht einem beliebigen Wert. In WithUnderscore, die In-Scope-Konstante _ bewirkt, dass der _ Arm als constant_pattern interpretiert wird, der nur dem Wert 0entspricht.

static string WithoutUnderscore(int n) => n switch
{
    1 => "one",
    _ => "other",
};

static string WithUnderscore(int n)
{
    const int _ = 0;
    return n switch
    {
        1 => "one",
        _ => "zero",
        var x => "other: " + x,
    };
}

Endbeispiel

11.2.8 Typmuster

Ein type_pattern wird verwendet, um zu testen, dass der Mustereingabewert (§11.1) einen bestimmten Typ aufweist.

type_pattern
    : type
    ;

Ein Typmuster, das einen Typ T benennt, gilt für jeden TypE, für den Edas Muster kompatibelT ist (§11.2.2).

Der Laufzeittyp des Werts wird anhand der im Is-Type-Operator angegebenen Regeln (§12.15.12.12.1) getestet. Wenn der Test erfolgreich ist, entspricht das Muster diesem Wert. Es handelt sich um einen Kompilierungszeitfehler, wenn der Typ ein nullwertbarer Typ ist. Dieses Musterformular entspricht niemals einem null Wert.

11.2.9 Relationales Muster

Ein relational_pattern wird verwendet, um den Mustereingabewert (§11.1) relational anhand eines Konstantenwerts zu testen.

relational_pattern
    : '<'  relational_expression
    | '<=' relational_expression
    | '>'  relational_expression
    | '>=' relational_expression
    ;

Die relational_expression in einem relational_pattern ist erforderlich, um einen konstanten Wert auszuwerten.

Relationale Muster unterstützen die relationalen Operatoren , , , und < auf allen integrierten Typen, die solche binären relationalen Operatoren mit beiden Operanden mit demselben Typ unterstützen: <=, >>=sbytebyte, short, ushortintuintlongulongchar, float, und doubleEnums. decimalnintnuint

Es handelt sich um einen Kompilierungszeitfehler, wenn constant_expressionist double.NaN, float.NaNoder null_literal.

Wenn der Eingabewert einen Typ aufweist, für den ein geeigneter integrierter binärer relationaler Operator definiert ist, wird die Auswertung dieses Operators als Bedeutung des relationalen Musters verwendet. Andernfalls wird der Eingabewert in den Typ der constant_expression mit einer expliziten NULL-Konvertierung oder Unboxing-Konvertierung konvertiert. Es handelt sich um einen Kompilierzeitfehler, wenn es keine solche Konvertierung gibt. Es handelt sich um einen Kompilierungszeitfehler, wenn der Eingabetyp ein Typparameter ist, der auf einen typvererbten System.Numerics.INumberBase<T> Typ beschränkt ist und der Eingabetyp keinen geeigneten binären relationalen Operator definiert hat. Das Muster wird als nicht übereinstimmend betrachtet, wenn die Konvertierung fehlschlägt. Wenn die Konvertierung erfolgreich ist, ist das Ergebnis des Musterabgleichsvorgangs das Ergebnis der Auswertung des Ausdrucks e «op» v , bei dem e es sich um die konvertierte Eingabe handelt, «op» ist der relationale Operator und v ist der constant_expression.

Beispiel:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(double.NaN));
Console.WriteLine(Classify(2.4));

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

Die erzeugte Ausgabe ist

Too high
Unknown
Acceptable

Endbeispiel

11.2.10 Logisches Muster

Ein logical_pattern wird verwendet, um das Ergebnis einer Musterabgleichung zu verwerfen oder die Ergebnisse mehrerer Mustervergleiche mithilfe von Konjunktion (and) oder Disjunktion (or) zu kombinieren.

logical_pattern
    : disjunctive_pattern
    ;

disjunctive_pattern
    : disjunctive_pattern 'or' conjunctive_pattern
    | conjunctive_pattern
    ;

conjunctive_pattern
    : conjunctive_pattern 'and' negated_pattern
    | negated_pattern
    ;

negated_pattern
    : 'not' negated_pattern
    | primary_pattern
    ;

not, andund or werden gemeinsam als Musteroperatoren bezeichnet.

Ein negated_pattern stimmt überein, wenn das negierte Muster nicht übereinstimmt und umgekehrt. Für eine conjunctive_pattern müssen beide Muster übereinstimmen. Ein disjunctive_pattern erfordert eine Übereinstimmung mit beiden Mustern. Im Gegensatz zu ihren Sprachoperator-Gegenstücken && und ||, and und or es handelt sich nicht um Kurzschlussoperatoren.

Es handelt sich um einen Kompilierungszeitfehler für eine Mustervariable, die unter einem oder not einem or Musteroperator deklariert werden soll.

Hinweis: Da weder notor eine bestimmte Zuordnung für eine Mustervariable erzeugt werden kann, ist es ein Fehler, einen in diesen Positionen zu deklarieren. Endnote

In einem conjunctive_pattern wird der Eingabetyp des zweiten Musters durch die Typeninschränkungsanforderungen des ersten Musters der and. Der schmale Typ eines Musters P wird wie folgt definiert:

  • Wenn P es sich um ein Typmuster handelt, ist der schmale Typ der Typ des Typs musters.
  • Andernfalls ist der schmale Typ der Typ des Deklarationsmusters, wenn P es sich um ein Deklarationsmuster handelt.
  • Wenn es sich um ein rekursives Muster handelt, das einen P.
  • Andernfalls ist der P, wenn ITuple die Regeln für eine positional_pattern (§11.2.5) abgeglichen werdenSystem.ITuple.
  • Andernfalls ist die Konstante ein Konstantenmuster, P bei dem die Konstante nicht die Nullkonstante ist und wenn der Ausdruck keine Konstantekonvertierung in den Eingabetyp aufweist, ist der schmale Typ der Konstante.
  • Andernfalls ist ein relationales Muster, P bei dem der Konstantenausdruck keine Konstantenausdruckkonvertierung in den Eingabetyp aufweist, ist der schmale Typ der Konstante.
  • P Andernfalls ist or der schmale Typ der schmalen Art der Unterpattern üblich, wenn ein solcher allgemeiner Typ vorhanden ist. Zu diesem Zweck berücksichtigt der allgemeine Typalgorithmus nur Identitäts-, Box- und implizite Verweiskonvertierungen und berücksichtigt alle Unterpattern einer Abfolge von or Mustern (ignoriert Klammermuster).
  • P Andernfalls ist bei einem and Muster der schmale Typ der schmale Typ des richtigen Musters. Darüber hinaus ist der schmale Typ des linken Musters der Eingabetyp des rechten Musters.
  • Andernfalls ist Pder schmale Typ des P Eingabetyps.

Hinweis: Wie durch die Grammatik angegeben, not hat Vorrang andvor , was Vorrang orhat. Dies kann mithilfe von Klammern explizit angegeben oder außer Kraft gesetzt werden. Endnote

Wenn ein Muster auf der rechten Seite des isMusters angezeigt wird, wird der Umfang des Musters durch die Grammatik bestimmt. Daher werden die Musteroperatoren and, orund not innerhalb des Musters enger als die logischen Operatoren &&, ||und ! außerhalb des Musters gebunden.

Beispiel:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(-100));
Console.WriteLine(Classify(5.7));

static string Classify(double measurement) => measurement switch
{
    < -40.0 => "Too low",
    >= -40.0 and < 0 => "Low",
    >= 0 and < 10.0 => "Acceptable",
    >= 10.0 and < 20.0 => "High",
    >= 20.0 => "Too high",
    double.NaN => "Unknown",
};

Die erzeugte Ausgabe ist

High
Too low
Acceptable

Endbeispiel

Beispiel:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    3 or 4 or 5 => "spring",
    6 or 7 or 8 => "summer",
    9 or 10 or 11 => "autumn",
    12 or 1 or 2 => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date),
      $"Date with unexpected month: {date.Month}."),
};

Die erzeugte Ausgabe ist

winter
autumn
spring

Endbeispiel

Beispiel:

object msg = "msg";
object obj = 5;
bool flag = true;

// This is parsed as: (msg is (not int) or string)
result = msg is not int or string;
Console.WriteLine($"msg (\"msg\"): msg is not int or string: {result}");

// This is parsed as: (obj is (int or string)) && flag
bool result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}");

flag = false;
// This is parsed as: (obj is (int or string)) && flag
result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}");

Die erzeugte Ausgabe ist

msg ("msg"): msg is not int or string: True
obj (5), flag (true): obj is int or string && flag: True
obj (5), flag (true): obj is int || obj is string && flag: True
obj (5), flag (false): obj is int or string && flag: False
obj (5), flag (false): obj is int || obj is string && flag: True

Endbeispiel

11.2.11 Listenmuster

Ein list_pattern entspricht einer Abfolge von Elementen in einer Liste oder einem Array.

list_pattern
    : list_pattern_clause simple_designation?
    ;

list_pattern_clause
    : '[' (pattern (',' pattern)* ','?)? ']'
    ;

Ein list_pattern ist mit jedem Typ kompatibel, der gezählt werden kann (§18.1) sowie indizierbar (§18.1) – es verfügt über einen barrierefreien Indexer, der ein Index Argument verwendet, oder einen barrierefreien Indexer mit einem einzelnen int Parameter. Wenn beide Indexer vorhanden sind, wird der frühere bevorzugt. (Einzelheiten zur impliziten Indexunterstützung finden Sie unter §18.4.2 .)

Ein Muster des Formulars expr is [1, 2, 3] entspricht dem folgenden Code:

expr.Length is 3
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Index(1, fromEnd: false)] is 2
&& expr[new Index(2, fromEnd: false)] is 3

Beispiel:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3 and not 7]);  // True

Endbeispiel

Das Verwerfen-Muster (§11.2.7) entspricht jedem einzelnen Element.

Beispiel:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [_, var second, _])
{
    Console.WriteLine($"The second element is {second}.");
}

Endbeispiel

11.2.12 Segmentmuster

Ein slice_pattern verwirft null oder mehr Elemente. Sie darf nur direkt in einer list_pattern_clause und dann nur einmal in dieser Klausel verwendet werden.

slice_pattern
    : '..' pattern?
    ;

Eine slice_pattern ohne Unterpattern ist mit jedem Typ kompatibel, der mit einem list_pattern kompatibel ist. Ein slice_pattern mit einem Unterpattern ist mit jedem Typ kompatibel, der gezählt werden kann (§18.1) sowie datenschnittfähig (§18.1) – es verfügt über einen barrierefreien Indexer, der ein Range Argument verwendet, oder eine barrierefreie Slice Methode mit zwei int Parametern. Wenn beide vorhanden sind, wird der frühere bevorzugt. (Einzelheiten zur impliziten Indexunterstützung finden Sie unter §18.4.2 .)

Ein slice_pattern verhält sich wie ein ordnungsgemäßes Verwerfen; d. h., es sind keine Prüfungen für ein solches Muster durchzuführen. Vielmehr betrifft sie nur andere Knoten, nämlich die Länge und den Indexer. Beispielsweise entspricht ein Muster des Formulars expr is [1, .. var s, 3] dem folgenden Code (sofern es über explizite Index und Range Support kompatibel ist):

expr.Length is >= 2
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s
&& expr[new Index(1, fromEnd: true)] is 3

Der Eingabetyp für eine slice_pattern ist der Rückgabetyp des zugrunde liegenden this[Range] oder Slice der Methode mit zwei Ausnahmen: Für strings und Arrays string.Substring bzw RuntimeHelpers.GetSubArray. werden verwendet.

Beispiel: Ein Segmentmuster kann verwendet werden, um Elemente nur am Anfang oder/und am Ende einer Eingabesequenz abzugleichen.

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

Endbeispiel

Beispiel: Ein Unterpattern kann in einem Segmentmuster geschachtelt werden:

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron"); // output: Message apron doesn't match.

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });     // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0]
        ? "valid" : "not valid";
    Console.WriteLine(result);
}

Endbeispiel

11.3 Musterunternahme

In einer Switch-Anweisung (§13.8.3) handelt es sich um einen Fehler, wenn das Muster eines Falls durch den vorherigen Satz nicht überwachter Fälle (§13.8.3) subsumiert wird. In einem Schalterausdruck (§12.12) handelt es sich um einen Fehler, wenn das Muster eines switch_expression_arm durch den vorherigen Satz von nicht überwachtenmustern switch_expression_armsubsumiert wird.

Hinweis: Dies bedeutet, dass jeder Eingabewert mit einem der vorherigen Fälle oder Arme abgeglichen wurde. Endnote Die folgenden Regeln definieren, wann eine Reihe von Mustern ein bestimmtes Muster subsumiert:

Ein Muster Pentspricht einer Konstante K , wenn eine der folgenden Bedingungen enthalten ist:

  • Die Spezifikation für das Laufzeitverhalten dieses Musters entspricht demP.K
  • P ist eine type_pattern für typ T und K nicht null und der Laufzeittyp K ist T oder ein Typ, der von T oder einem Typ abgeleitet ist, der implementiert Twird.
  • Pist eine relational_pattern mit Operator «op» und konstantv, und der Ausdruck K «op» v würde ausgewertet.true
  • P ist eine negated_patternnot P₁ und P₁ würde nicht übereinstimmen K.
  • P ist eine conjunctive_patternP₁ and P₂ , und beide P₁ würden übereinstimmen K und P₂ übereinstimmen K.
  • P ist eine disjunctive_patternP₁ or P₂ und würde entweder P₁ übereinstimmen K oder P₂ übereinstimmen K.
  • P ist ein discard_pattern.

Eine Reihe von Mustern Qsubsumiert ein Muster P , wenn eine der folgenden Bedingungen enthalten ist:

  • Pist ein konstantes Muster, und jedes der Muster in der Gruppe Q würde mit dem konvertierten Wert übereinstimmenP.
  • Pist ein Var-Muster, und der Satz von Mustern Q ist erschöpfend (§11.4) für den Typ des Mustereingabewerts (§11.1), und entweder ist der Mustereingabewert kein nullabler Typ oder ein Muster Q in übereinstimmungen.null
  • Pist ein Deklarationsmuster mit Typ T und der Satz von Mustern Q ist (T).
  • Pist eine type_pattern für typT, und der Satz von Mustern Q ist für den Typ T.
  • P ist eine relational_pattern mit Operator «op» und konstantem Wert v, und für jeden Wert des Eingabetyps, der die Beziehung «op» verfüllt, würde ein Muster in der Menge Q diesem Wert entsprechen.
  • P ist eine disjunctive_patternP₁ or P₂ und die Gruppe von Mustern Q subsumes P₁ und Q Subsumes P₂.
  • P ist eine conjunctive_patternP₁ and P₂ und mindestens eine der folgenden Haltebereiche: Q Subsumes P₁oder Q Subsumen P₂.
  • Pist ein negated_patternnot P₁ und Q ist erschöpfend für den Eingabetyp, der nur die Werte berücksichtigt, die nicht übereinstimmen.P₁
  • P ist eine discard_pattern und der Satz von Mustern Q ist für den Typ des Mustereingabewerts erschöpfend , und entweder ist der Mustereingabewert kein nullabler Typ oder ein Muster in Q übereinstimmungen null.
  • Bei einigen Mustern handelt es sich um Q eine disjunctive_patternQ₁ or Q₂ und das Ersetzen dieses Musters durch Q₁Q in einen Satz würde einen Satz ergeben, der subsumiert Poder durch Q₂ ersetzt wird, einen Satz dieser Untersummen P.
  • Bei einigen Mustern handelt es sich um Q eine negated_patternnot Q₁ und P würde keinem Wert entsprechen, der Q₁ übereinstimmen würde.

Beispiel: Im folgenden Schalterausdruck wird kein Arm subsumiert, obwohl die Arme 1, 2 und 3 das gleiche Muster aufweisen:

object x = 10;
bool b = false;
int y = x switch
{
    int i when !b => 0,
    int i when b  => 1,
    int i         => 2,
    _             => 3
};

Arme 1 und 2 haben nichtkonstante Wachen und sind daher nicht geschützt; nur Arm 3 ist mit Musternint igeschützt, was den letzten _ Arm nicht subsumiert, da er nicht mit einem Nicht-Wertint übereinstimmt, wie z null. B. . Endbeispiel

Die Subvermutungsüberprüfung von list_patterns und slice_patternfunktioniert genauso wie Positionsmuster mit ITuple (§11.2.5) entsprechenden Unterpattern werden nach Position und einem zusätzlichen Knoten für die Testlänge abgeglichen.

Der folgende Code erzeugt beispielsweise einen Fehler, da beide Muster dieselbe DAG liefern:

case [_, .., 1]: // expr.Length is >= 2 && expr[^1] is 1
case [.., _, 1]: // expr.Length is >= 2 && expr[^1] is 1

Anders als:

case [_, 1, ..]: // expr.Length is >= 2 && expr[1] is 1
case [.., 1, _]: // expr.Length is >= 2 && expr[^2] is 1

Angesichts einer bestimmten Länge ist es möglich, dass zwei Unterpattern auf dasselbe Element verweisen, in diesem Fall wird ein Test für diesen Wert in die Entscheidung DAG eingefügt.

  • Wird [_, >0, ..] or [.., <=0, _] beispielsweise der Ort, length >= 2 && ([1] > 0 || length == 3 || [^2] <= 0) an dem der Längenwert von 3 den anderen Test impliziert.
  • Umgekehrt wird [_, >0, ..] and [.., <=0, _] der Ort, length >= 2 && [1] > 0 && length != 3 && [^2] <= 0 an dem der Längeswert von 3 den anderen Test nicht zulässig ist.

Daher führt ein Fehler zu einem Ähnlichen case [.., p]: case [p]: , weil zur Laufzeit dasselbe Element im zweiten Fall abgeglichen wird.

Wenn ein Segment-Unterpattern einer Liste oder einem Längenwert entspricht, müssen Unterpattern so behandelt werden, als wären sie eine direkte Unterpattern der enthaltenden Liste. Subsumiert z. B [..[1, 2, 3]] . ein Muster des Formulars [1, 2, 3].

Die folgenden Annahmen werden für die verwendeten Mitglieder gemacht:

  • Die Eigenschaft, mit der der Typ gezählt werden kann , wird angenommen, dass immer ein nicht negativer Wert zurückgegeben wird, wenn der Typ indiziert werden kann. Beispielsweise kann das Muster { Length: -1 } nie mit einem Array übereinstimmen.
  • Das Element, das den Typ segmentierbar macht, wird angenommen, dass er sich gut verhält; d. h., der Rückgabewert ist nie null und dass es sich um eine ordnungsgemäße Unterlizenz der enthaltenden Liste handelt.

Das Verhalten eines Musterabgleichsvorgangs ist nicht definiert, wenn eine der oben genannten Annahmen nicht enthalten ist. [

11.4 Mustererschöpfend

Informell ist eine Reihe von Mustern für einen Typ erschöpfend, wenn für jeden möglichen Wert dieses Typs außer NULL ein bestimmtes Muster in der Menge anwendbar ist. Die folgenden Regeln definieren, wann eine Reihe von Mustern für einen Typ erschöpfend ist:

Eine Reihe von Mustern Q ist für einen Typ erschöpfendT:

  1. T ist ein integraler oder enumerationstyp oder eine nullable Version einer dieser Typen, und für jeden möglichen Wert des Tnicht nullablen zugrunde liegenden Typs würde ein Muster Q mit diesem Wert übereinstimmen; oder
  2. Bei einigen Mustern handelt es sich um Q ein Var-Muster; oder
  3. Bei einigen Mustern Q handelt es sich um ein Deklarationsmuster für den Typ D, und es gibt eine Identitätskonvertierung, eine implizite Verweiskonvertierung oder eine Boxumwandlung von T zu D; oder
  4. Bei einigen Mustern Q handelt es sich um eine type_pattern für den Typ D, und es gibt eine Identitätskonvertierung, eine implizite Verweiskonvertierung oder eine Boxumwandlung von T zu D; oder
  5. Bei einigen Q handelt es sich um eine discard_pattern; oder
  6. Die Muster Q enthalten eine Kombination aus relational_patterns und constant_patterns, deren Bereiche zusammen jeden möglichen Wert des Tnicht nullablen zugrunde liegenden Typs abdecken. Für float und double Typen umfasst System.Double.NaN dies bzw System.Single.NaN . dies, da NaN nicht mit einem relationalen Muster abgeglichen wird;
  7. Bei einigen Mustern handelt es sich um Q eine disjunctive_patternP₁ or P₂ und das Ersetzen dieses Musters durch sowohl als P₁ auch P₂ durch einen Q Satz, der erschöpfend istT; oder
  8. Bei einigen Mustern handelt es sich um Q eine negated_patternnot P₁ und die Muster zusammen Q mit den Werten, die nicht mit P₁ jedem möglichen Wert übereinstimmen T. Ein negated_patternnot P₁ ist selbst erschöpfend, wenn P₁ er keinen möglichen Wert von T; oder
  9. Bei einigen Mustern handelt es sich um Q eine conjunctive_patternP₁ and P₂, und der Satz, der nur P₁ erschöpfend ist, und der Satz, der nur T ist, ist P₂ für T.

Hinweis: Wenn ein Typmuster nullwerte Typen enthält, kann das Muster für den Typ erschöpfend sein, aber dennoch eine Warnung generieren, da das Typmuster nicht mit einem null Wert übereinstimmt. Endnote

Hinweis: Bei Gleitkommatypen ist die Kombination von Mustern < 0 und >= 0nicht erschöpfend, da keine relationalen Muster übereinstimmen NaN. Ein korrekter erschöpfender Satz wäre < 0, >= 0und double.NaN (oder float.NaN). Endnote

Beispiel:

static void M(byte b)
{
    switch (b) {
        case 0: case 1: case 2: ... // handle every specific value of byte
            break;
        // error: the pattern 'byte other' is subsumed by the (exhaustive)
        // previous cases
        case byte other: 
            break;
    }
}

Endbeispiel