Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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.
Ein Muster P wird von einem Satz von nicht überwachten Mustern Qsubsumiert, wenn ein Eingabewert, der mit P einem der Member Qübereinstimmt, abgeglichen wird.
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.
Eine Reihe von Mustern ist erschöpfend, wenn für jeden möglichen Eingabewert ein muster in der Menge anwendbar ist. Wenn eine Implementierung erkennt, dass eine Reihe von Mustern nicht erschöpfend ist, wird eine Warnung ausgegeben.
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
| list_pattern
| slice_pattern
;
parenthesized_pattern
: '(' 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
vlautetTextReader. Eine Variable vom TypTextReaderkann niemals einen Wert aufweisen, der referenzkompatibel ist mitstring: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
vlautetobject. Eine Variable des Typsobjectkönnte einen Wert aufweisen, der referenzkompatibel ist mitstring: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
Es handelt sich um einen Kompilierungszeitfehler, wenn der Typ ein Nullwerttyp (§8.3.12) oder ein nullabler Bezugstyp (§8.9.3) ist.
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.
Hinweis: Der Is-Type-Ausdruck
e is Tund das Deklarationsmustere is T _sind gleichwertig, wenn beide gültig sind. 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 BoxedT) entspricht einem TypmusterT2 id, wenn der Wert ungleich NULL ist undT2Toder ein Basistyp oder eine Schnittstelle vonT. Beispiel: im Codefragmentint? x = 3; if (x is int v) { /* code using v */ }Die Bedingung der
ifAnweisung befindettruesich zur Laufzeit, und die Variableventhält den Wert3des Typsintinnerhalb des Blocks. Nach dem Block befindet sich die Variablevim 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 TypT, wenn eine implizite Konvertierung aus dem konstanten Ausdruck des P Typs Tvorhanden ist oder ob System.Span<char>T oder System.ReadOnlySpan<char> und der konstante Ausdruck P vom Typ string ist und nicht das null Literal 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, entspricht
Pdem Wert e, wenn das Ergebnis des Ausdruckse == vlautettrue; andernfalls - ist e vom Typ
System.Span<char>oderSystem.ReadOnlySpan<char>, und v ist eine konstante Zeichenfolge, und v hat keinen konstanten Wert vonnull, das MusterPentspricht dem Wert e ifSystem.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(v))returnstrue; andernfalls - das Muster
Pentspricht dem Wert e , wennobject.Equals(e, v)zurückgegeben wirdtrue.
Beispiel: Die
switchAnweisung 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
Deconstructunterscheidet 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.
- 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.
- Deconstruct form. Andernfalls gilt, wenn der Typ vorhanden ist oder der Typ ausgelassen wird und der statische Typ des Eingabewerts eine barrierefreie
DeconstructMethode (§12.7) enthält. Geben Sie Dein , wenn der Typ vorhanden ist; andernfalls ist D der statische Typ des Eingabewerts. EineDeconstructMethode wird aus D unter Verwendung der gleichen Überladungsauflösungsregeln wie für eine Dekonstruktionsdeklaration ausgewählt, wobei die zusätzliche Anforderung, dass die Anzahl deroutParameter 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ählteDeconstructMethode wird mit neuen Variablen aufgerufen, die ihreoutParameter 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 vonDeconstruct. - 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 VerweiskonvertierungSystem.Runtime.CompilerServices.ITupleenthält , und dieser Fall gilt. Zur Laufzeit wird der Eingabewert als eine NichtinstanznullvonSystem.Runtime.CompilerServices.ITuple; wenn dieser Test fehlschlägt, schlägt die Positionsmustervergleich fehl. Andernfalls wird die Eigenschaft des WertsLengthgelesen, 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. - 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 (SumAndAverage(numbers) is (Sum: var sum, Average: var average)) { Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}; average is {average}"); } else { // Note: sum and average are in scope here, but not definitely assigned Console.WriteLine("No numbers provided to compute sum and average."); } static (double Sum, double Average)? SumAndAverage(IEnumerable<int> numbers) { int sum = 0; int count = 0; foreach (int number in numbers) { sum += number; count++; } return count == 0 ? null : (sum, sum / count); }Die erzeugte Ausgabe ist
Sum of [10 20 30] is 60; average is 20Endbeispiel
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
sungleich 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 {}) ...Das Deklarieren
x2des Beispiels ähneltif (s is var x2)in Bezug auf das Ableiten des Variablentyps, das Eigenschaftenmuster garantiert jedoch, dassx2es sich nicht um Null handelt. Endnote
Bei einer Übereinstimmung eines Ausdrucks e mit denUnterpattern des {} 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 abcEndbeispiel
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 einesisOperators 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_eines declaration_pattern (z. B. ) oder in anderen Musterformen angezeigt werden,_ xdie 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.0Hier wird ein Verwerfenmuster verwendet, um einen ganzzahligen Wert zu behandeln
null, der nicht über das entsprechende Element derDayOfWeekEnumeration verfügt. Dadurch wird sichergestellt, dass derswitchAusdruck alle möglichen Eingabewerte verarbeitet. Endbeispiel
Beispiel: Im Folgenden wird veranschaulicht, wie eine In-Scope-Konstante mit dem Namen
_die Interpretation eines_Arms in einemswitchAusdruck ändert. InWithoutUnderscore, ist der_Arm ein discard_pattern und entspricht einem beliebigen Wert. InWithUnderscore, die In-Scope-Konstante_bewirkt, dass der_Arm als constant_pattern interpretiert wird, der nur dem Wert0entspricht.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
Eine relational_patterngilt für einen Typ T , wenn ein geeigneter integrierter binärer relationaler Operator mit beiden Operanden des Typs Tdefiniert ist, oder wenn eine explizite nullable oder unboxing Konvertierung vom T Typ des Konstantenausdrucks vorhanden ist.
Es handelt sich um einen Kompilierungsfehler, wenn der Ausdruck als double.NaN, float.NaNoder eine NULL-Konstante ausgewertet wird.
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 mithilfe einer expliziten NULL-Konvertierung oder Unboxing-Konvertierung in den Typ des Konstantenausdrucks konvertiert. Es handelt sich um einen Kompilierzeitfehler, wenn es keine solche Konvertierung gibt. 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 konstante Ausdruck.
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 AcceptableEndbeispiel
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
notoreine 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
Pes sich um ein Typmuster handelt, ist der schmale Typ der Typ des Typs musters. - Andernfalls ist der
Pder Typ des Deklarationsmusters, wenn es sich um ein Deklarationsmuster handelt. - Wenn es sich um ein rekursives Muster handelt, das einen
P. - Andernfalls ist der
P, wennITupledie Regeln für eine positional_pattern (§11.2.5) abgeglichen werdenSystem.ITuple. - Andernfalls ist die Konstante ein Konstantenmuster,
Pbei 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,
Pbei dem der Konstantenausdruck keine Konstantenausdruckkonvertierung in den Eingabetyp aufweist, ist der schmale Typ der Konstante. -
PAndernfalls istorder 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 vonorMustern (ignoriert Klammermuster). -
PAndernfalls ist bei einemandMuster 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 der
PdesPEingabetyps.
Hinweis: Wie durch die Grammatik angegeben,
nothat Vorrangandvor , was Vorrangorhat. 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 AcceptableEndbeispiel
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 springEndbeispiel
Beispiel:
object msg = "msg"; object obj = 5; bool flag = true; // This is parsed as: (msg is (not int) or string) bool 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 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: TrueEndbeispiel
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]); // TrueEndbeispiel
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]); // FalseEndbeispiel
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
ECMA C# draft specification