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 ist eine syntaktische Form, die mit dem is Operator (§12.14.12) in einem switch_statement (§13.8.3) und in einem switch_expression (§12.11) verwendet werden kann, um das Shape der Daten auszudrücken, mit denen eingehende Daten verglichen werden sollen. Muster können rekursiv sein, sodass Teile der Daten mit Untermustern abgeglichen werden können.
Ein Muster wird anhand eines Werts in einer Reihe von Kontexten getestet:
- In einer Switch-Anweisung wird das Muster einer Fallbezeichnung anhand des Ausdrucks der Switch-Anweisung getestet.
- Bei einem Is-Pattern-Operator wird das Muster auf der rechten Seite mit dem Ausdruck auf der linken Seite getestet.
- In einem Schalterausdruck wird das Muster eines switch_expression_arm auf der linken Seite des Schalterausdrucks anhand des Ausdrucks 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
: declaration_pattern
| constant_pattern
| var_pattern
| positional_pattern
| property_pattern
| discard_pattern
;
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 Deconstruct, Eigenschaftenzugriffe und Aufrufe von Methoden in System.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
;
Ein simple_designation mit dem Token _ gilt als discard_designation und nicht als single_variable_designation.
Der Laufzeittyp des Werts wird anhand der gleichen Regeln getestet, die im Is-Type-Operator (§12.14.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 Tund das Deklarationsmustere is T _sind gleichwertig, wennTkein Nullwerttyp vorhanden ist. Endnote
Bei einem Mustereingabewert (§11.1) e wird, wenn die simple_designationdiscard_designation ist, ein Verwerfen (§9.2.9.2) bezeichnet, und der Wert von e ist an nichts gebunden. (Obwohl eine deklarierte Variable mit dem Namen _ zu diesem Zeitpunkt im Bereich enthalten sein kann, wird diese benannte Variable in diesem Kontext nicht angezeigt.) Andernfalls wird eine lokale Variable (§9.2.9) des angegebenen Typs eingeführt, wenn der simple_designationsingle_variable_designation ist. Diese lokale Variable wird dem Wert des Mustereingabewerts zugewiesen, wenn das Muster dem Wert entspricht .
Bestimmte Kombinationen des statischen Typs des Mustereingabewerts und des angegebenen Typs werden als inkompatibel betrachtet und führen zu einem Kompilierungszeitfehler. Ein Wert des statischen Typs E wird als Muster angegeben, das mit dem Typ kompatibel, wenn eine Identitätskonvertierung, eine implizite oder explizite Verweiskonvertierung, eine Boxkonvertierung oder eine Unboxing-Konvertierung von T zu E, oder wenn TE es sich um einen geöffneten Typ (T) handelt. Ein Deklarationsmuster, das einen Typ T benennt, gilt für jeden Typ E , für den E das Muster kompatibel ist T.
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 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, entspricht
Pdem Wert e, wenn das Ergebnis des Ausdruckse == vlautettrue; 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 Formularbezeichnung(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, ruft eine geeignete Deconstruct Methode (§12.7) auf und führt einen weiteren Musterabgleich für die resultierenden Werte durch. Es unterstützt auch eine Tupel-ähnliche Mustersyntax (ohne den Typ, der bereitgestellt wird), wenn der Typ des Eingabewerts mit dem Typ übereinstimmt, der den Typ des Eingabewerts enthältDeconstruct, oder wenn der Typ des Eingabewerts ein Tupeltyp ist oder ob der Typ des Eingabewerts oder System.ITuple der Laufzeittyp des Ausdrucks System.ITupleimplementiert wirdobject.
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern (',' subpattern)*
;
subpattern
: pattern
| identifier ':' pattern
;
Wenn eine Übereinstimmung eines Eingabewerts mit denUnterpattern des Mustertyps() übereinstimmt, wird eine Methode ausgewählt, indem sie im Typ nach barrierefreien Deklarationen Deconstruct suchen und eine davon auswählen, die dieselben Regeln wie für die Dekonstruktionsdeklaration verwendet.
Es ist ein Fehler, wenn ein positional_pattern den Typ ausgelassen, einen einzelnen Unterpattern ohne Bezeichner hat, keine property_subpattern hat und keine simple_designation hat. Dies unterscheidet sich zwischen einer constant_pattern , die Klammer und eine positional_pattern ist.
Um die Werte zu extrahieren, die mit den Mustern in der Liste übereinstimmen sollen,
- Wenn der Typ ausgelassen wird und der Typ des Eingabeausdrucks ein Tupeltyp ist, muss die Anzahl der Unterpattern mit der Kardinalität des Tupels identisch sein. Jedes Tupelelement wird mit dem entsprechenden Unterpattern abgeglichen, und die Übereinstimmung ist erfolgreich, wenn alle diese Elemente erfolgreich sind. Wenn ein Unterpattern über einen Bezeichner verfügt, muss dieser ein Tupelelement an der entsprechenden Position im Tupeltyp benennen.
- Andernfalls handelt es sich bei einem geeigneten
DeconstructElement des Typs um einen Kompilierungszeitfehler, wenn der Typ des Eingabewerts nicht musterkompatibel mit dem Typ ist. Zur Laufzeit wird der Eingabewert anhand des Typs getestet. Wenn dies fehlschlägt, schlägt die Positionsmustervergleich fehl. Wenn er erfolgreich ist, wird der Eingabewert in diesen Typ konvertiert undDeconstructmit neu generierten Variablen aufgerufen, um die Ausgabeparameter zu empfangen. 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 einen Parameter an der entsprechenden Position vonDeconstruct. - Andernfalls wird kein Typ angegeben, und der Eingabewert ist vom Typ
objectoder einem Typ, der von einer impliziten Verweiskonvertierung in konvertiertSystem.ITuplewerden kann, und kein Bezeichner unter den Unterpattern angezeigt wird, wird die Übereinstimmung verwendetSystem.ITuple. - Andernfalls ist das Muster 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 60Endbeispiel
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 keinen Bezeichner 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 {}) ...Endnote Angesichts einer Übereinstimmung eines Ausdrucks e mit dem Mustertyp
{property_pattern_list}, handelt es sich um einen Kompilierungszeitfehler, wenn der Ausdruck e nicht musterkompatibel mit dem typ T ist, der nach Typ festgelegt ist. Wenn der Typ nicht vorhanden ist, wird der Typ als statischer Typ von e angenommen. Jeder der Bezeichner, die auf der linken Seite seines property_pattern_list angezeigt werden, 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.
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
: '_'
;
Es handelt sich um einen Kompilierungsfehler, um ein Verwerfenmuster in einem relational_expression des Formulars relational_expressionisMusters oder als Muster einer switch_label zu verwenden.
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
11.3 Musterunternahme
Bei einer Switch-Anweisung handelt es sich um einen Fehler, wenn das Muster eines Falls von der vorherigen Gruppe nicht überwachter Fälle (§13.8.3) subsumiert wird. Informell bedeutet dies, dass jeder Eingabewert mit einem der vorherigen Fälle abgeglichen worden wäre. Die folgenden Regeln definieren, wann eine Reihe von Mustern ein bestimmtes Muster subsumiert:
Ein Muster P stimmt mit einer Konstante überein, wenn die Spezifikation für das Laufzeitverhalten K dieses Musters mit übereinstimmungtP.K
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 GruppeQwürde mit dem konvertierten Wert übereinstimmenP. -
Pist ein Var-Muster, und der Satz von MusternQist erschöpfend (§11.4) für den Typ des Mustereingabewerts (§11.1), und entweder ist der Mustereingabewert kein nullabler Typ oder ein MusterQin übereinstimmungen.null -
Pist ein Deklarationsmuster mit TypTund der Satz von MusternQist (T).
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:
-
Tist ein integraler oder enumerationstyp oder eine nullable Version einer dieser Typen, und für jeden möglichen Wert desTnicht nullablen zugrunde liegenden Typs würde ein MusterQmit diesem Wert übereinstimmen; oder - Bei einigen Mustern handelt es sich um
Qein Var-Muster; oder - Bei einigen Mustern
Qhandelt es sich um ein Deklarationsmuster für den TypD, und es gibt eine Identitätskonvertierung, eine implizite Verweiskonvertierung oder eine Boxumwandlung vonTzuD.
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
ECMA C# draft specification