11 Muster und Musterabgleich
11.1 Allgemein
Ein Muster ist eine syntaktische Form, die mit dem is
Operator (§12.12.12) und in einem switch_statement (§13.8.3) verwendet werden kann, um das Shape der Daten auszudrücken, mit denen eingehende Daten verglichen werden sollen. Ein Muster wird auf den Ausdruck einer Switch-Anweisung oder auf eine relational_expression getestet, die sich auf der linken Seite eines is
Operators befindet, von denen jeder als Mustereingabewert bezeichnet wird.
11.2 Musterformulare
11.2.1 Allgemein
Ein Muster kann eine der folgenden Formen aufweisen:
pattern
: declaration_pattern
| constant_pattern
| var_pattern
;
Ein declaration_pattern und ein var_pattern kö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
lautetTextReader
. Eine Variable vom TypTextReader
kann 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
v
lautetobject
. Eine Variable des Typsobject
kö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 .
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, den Wert in einer Variablen dieses Typs bereitstellen.
declaration_pattern
: type simple_designation
;
simple_designation
: single_variable_designation
;
single_variable_designation
: identifier
;
Der Laufzeittyp des Werts wird anhand der im Is-Type-Operator (§12.12.12.12.12.1) angegebenen Regeln im Muster getestet. Wenn der Test erfolgreich ist, entspricht das Muster diesem Wert. Es handelt sich um einen Kompilierungszeitfehler, wenn der Typ ein Nullwerttyp ist (§8.3.12). Dieses Musterformular entspricht niemals einem null
Wert.
Hinweis: Der Is-Type-Ausdruck
e is T
und das Deklarationsmustere is T _
sind gleichwertig, wennT
kein Nullwerttyp vorhanden ist. Endnote
Bei einem Mustereingabewert (§11.1) e, wenn die simple_designation der Bezeichner _
ist, wird ein Verwerfen (§9.2.9.1) und der Wert von e nicht 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.) Wenn simple_designation ein anderer Bezeichner ist, wird eine lokale Variable (§9.2.9) des durch den angegebenen Bezeichner benannten Typs eingeführt. 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 T
kompatibel ist, wenn eine Identitätskonvertierung, eine implizite oder explizite Verweiskonvertierung, eine Boxkonvertierung oder eine Unboxing-Konvertierung von E
zu T
, oder wenn T
E
es sich um einen geöffneten Typ (§8.4.3) 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
Es handelt sich um einen Fehler, wenn der Typ ein Nullwerttyp ist.
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 undT2
T
oder ein Basistyp oder eine Schnittstelle vonT
. Beispiel: im Codefragmentint? x = 3; if (x is int v) { /* code using v */ }
Die Bedingung der
if
Anweisung befindettrue
sich zur Laufzeit, und die Variablev
enthält den Wert3
des Typsint
innerhalb des Blocks. 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 T
vorhanden ist.
Bei einem Konstantenmuster P
ist 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 das Muster
P
dem Wert e, wenn das Ergebnis des Ausdruckse == v
lautettrue
; andernfalls - das Muster
P
entspricht dem Wert e , wennobject.Equals(e, v)
zurückgegeben wirdtrue
.
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_pattern entspricht 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
;
Bei einem Mustereingabewert (§11.1) e, wenn die Bezeichnung der Bezeichner _
ist, wird ein Verwerfen (§9.2.9.1) 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.) Wenn die Bezeichnung ein anderer Bezeichner ist, ist der Wert von e zur Laufzeit 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.
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 K
überein, wenn die Spezifikation für das Laufzeitverhalten P
dieses Musters mit übereinstimmungtK
.
Eine Reihe von Mustern Q
subsumiert ein Muster P
, wenn eine der folgenden Bedingungen enthalten ist:
P
ist ein konstantes Muster, und jedes der Muster in der GruppeQ
würde mit dem konvertierten Wert übereinstimmenP
.P
ist ein Var-Muster, und der Satz von MusternQ
ist erschöpfend (§11.4) für den Typ des Mustereingabewerts (§11.1), und entweder ist der Mustereingabewert kein nullabler Typ oder ein MusterQ
in übereinstimmungen.null
P
ist ein Deklarationsmuster mit TypT
und der Satz von MusternQ
ist für den TypT
erschöpfend (§11.4).
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 T
erschöpfend, wenn eine der folgenden Bedingungen enthalten ist:
T
ist ein integraler oder enumerationstyp oder eine nullable Version einer dieser Typen, und für jeden möglichen Wert desT
nicht nullablen zugrunde liegenden Typs würde ein MusterQ
mit diesem Wert übereinstimmen; oder- Bei einigen Mustern handelt es sich um
Q
ein Var-Muster; oder - Bei einigen Mustern
Q
handelt es sich um ein Deklarationsmuster für den TypD
, und es gibt eine Identitätskonvertierung, eine implizite Verweiskonvertierung oder eine Boxumwandlung vonT
zuD
.
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