Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
11.1 Général
Un modèle est une forme syntaxique qui peut être utilisée avec l’opérateur is (§12.14.12), dans un switch_statement (§13.8.3) et dans un switch_expression (§12.11) pour exprimer la forme de données sur laquelle les données entrantes doivent être comparées. Les modèles peuvent être récursifs, de sorte que les parties des données peuvent être mises en correspondance par rapport aux sous-modèles.
Un modèle est testé par rapport à une valeur dans un certain nombre de contextes :
- Dans une instruction switch, le modèle d’une étiquette de casse est testé par rapport à l’expression de l’instruction switch.
- Dans un opérateur is-pattern , le modèle situé à droite est testé par rapport à l’expression située à gauche.
- Dans une expression switch, le modèle d’un switch_expression_arm est testé par rapport à l’expression sur le côté gauche de l’expression switch-expression.
- Dans les contextes imbriqués, le sous-modèle est testé par rapport aux valeurs récupérées à partir de propriétés, de champs ou indexées à partir d’autres valeurs d’entrée, en fonction du formulaire de modèle.
La valeur par rapport à laquelle un modèle est testé est appelée valeur d’entrée de modèle.
11.2 Formulaires de modèle
11.2.1 Général
Un modèle peut avoir l’une des formes suivantes :
pattern
: declaration_pattern
| constant_pattern
| var_pattern
| positional_pattern
| property_pattern
| discard_pattern
;
Certains modèlespeuvent entraîner la déclaration d’une variable locale.
Chaque formulaire de modèle définit l’ensemble de types pour les valeurs d’entrée auxquelles le modèle peut être appliqué. Un modèle P s’applique à un type T s’il T se trouve parmi les types dont les valeurs peuvent correspondre au modèle. Il s’agit d’une erreur au moment de la compilation si un modèle P apparaît dans un programme pour correspondre à une valeur d’entrée de modèle (§11.1) du type T s’il P n’est pas applicable.T
Exemple : l’exemple suivant génère une erreur au moment de la compilation, car le type de compilation est
vTextReader. Une variable de typeTextReaderne peut jamais avoir de valeur compatible référence avecstring:TextReader v = Console.In; // compile-time type of 'v' is 'TextReader' if (v is string) // compile-time error { // code assuming v is a string }Toutefois, les éléments suivants ne génèrent pas d’erreur au moment de la compilation, car le type de compilation est
vobject. Une variable de typeobjectpeut avoir une valeur compatible référence avecstring:object v = Console.In; if (v is string s) { // code assuming v is a string }exemple de fin
Chaque formulaire de modèle définit l’ensemble de valeurs pour lesquelles le modèle correspond à la valeur au moment de l’exécution.
L’ordre d’évaluation des opérations et des effets secondaires pendant la correspondance des modèles (appels aux Deconstructaccès aux propriétés et appels de méthodes dans System.ITuple) n’est pas spécifié.
Modèle de déclaration 11.2.2
Une declaration_pattern est utilisée pour tester qu’une valeur a un type donné et, si le test réussit, pour fournir éventuellement la valeur dans une variable de ce type.
declaration_pattern
: type simple_designation
;
simple_designation
: discard_designation
| single_variable_designation
;
discard_designation
: '_'
;
single_variable_designation
: identifier
;
Une simple_designation avec le jeton _ doit être considérée comme une discard_designation plutôt qu’une single_variable_designation.
Le type d’exécution de la valeur est testé par rapport au type dans le modèle à l’aide des mêmes règles spécifiées dans l’opérateur is-type (§12.14.12.1). Si le test réussit, le modèle correspond à cette valeur. Il s’agit d’une erreur au moment de la compilation si le type est un type valeur nullable (§8.3.12) ou un type référence nullable (§8.9.3). Ce formulaire de modèle ne correspond jamais à une null valeur.
Remarque : L’expression
e is Tis-type et le modèlee is T _de déclaration sont équivalents lorsqu’ilTn’est pas un type Nullable. Note de fin
Étant donné une valeur d’entrée de modèle (§11.1) e, si le simple_designation est discard_designation, il désigne un abandon (§9.2.9.2) et la valeur e n’est liée à rien. (Bien qu’une variable déclarée portant le nom _ puisse être dans l’étendue à ce stade, cette variable nommée n’est pas vue dans ce contexte.) Sinon, si le simple_designation est single_variable_designation, une variable locale (§9.2.9) du type donné nommé par l’identificateur donné est introduite. Cette variable locale est affectée à la valeur d’entrée du modèle lorsque le modèle correspond à la valeur.
Certaines combinaisons de type statique de la valeur d’entrée du modèle et du type donné sont considérées comme incompatibles et entraînent une erreur au moment de la compilation. Une valeur de type E statique est considérée comme compatible avec le type T s’il existe une conversion d’identité, une conversion de référence implicite ou explicite, une conversion de boxe ou une conversion d’unboxing à Epartir T de , ou s’il ET s’agit d’un type ouvert (§8.4.3). Un modèle de déclaration nommant un type s’applique T chaque type pour lequel E le modèle est compatible avec E.T
Remarque : La prise en charge des types ouverts peut être la plus utile lors de la vérification des types qui peuvent être des types de structure ou de classe, et la boxe doit être évitée. Note de fin
Exemple : Le modèle de déclaration est utile pour effectuer des tests de type d’exécution de types référence et remplacer l’idiome
var v = expr as Type; if (v != null) { /* code using v */ }avec le plus concis
if (expr is Type v) { /* code using v */ }exemple de fin
Exemple : le modèle de déclaration peut être utilisé pour tester des valeurs de types Nullables : une valeur de type
Nullable<T>(ou une zoneT) correspond à un modèleT2 idde type si la valeur est non null etT2estT, ou un type de base ou une interface deT. Par exemple, dans le fragment de codeint? x = 3; if (x is int v) { /* code using v */ }La condition de l’instruction est
ifau moment de l’exécutiontrueet la variablevcontient la valeur3du typeintà l’intérieur du bloc. Une fois que le bloc de la variablevest dans l’étendue, mais pas définitivement affecté. exemple de fin
Modèle de constante 11.2.3
Un constant_pattern est utilisé pour tester la valeur d’une valeur d’entrée de modèle (§11.1) par rapport à la valeur constante donnée.
constant_pattern
: constant_expression
;
Un modèle P constant s’applique à un type T s’il existe une conversion implicite de l’expression constante du P type T.
Pour un modèle Pconstant, sa valeur convertie est
- si le type de la valeur d’entrée de modèle est un type intégral ou un type d’énumération, la valeur constante du modèle convertie en ce type ; autrement
- si le type de la valeur d’entrée de modèle est la version nullable d’un type intégral ou d’un type enum, la valeur constante du modèle convertie en son type sous-jacent ; autrement
- valeur de la valeur constante du modèle.
Compte tenu d’une valeur d’entrée de modèle e et d’un modèle P constant avec la valeur convertie v,
- si e a un type intégral ou un type enum, ou une forme Nullable d’un de ces types, et v a un type intégral, le modèle
Pcorrespond à la valeur e si le résultat de l’expressione == vesttrue; sinon, - le modèle
Pcorrespond à la valeur e siobject.Equals(e, v)elle est retournéetrue.
Exemple : L’instruction
switchde la méthode suivante utilise cinq modèles constants dans ses étiquettes de cas.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(...); } }exemple de fin
Modèle var 11.2.4
Une var_patterncorrespond à chaque valeur. Autrement dit, une opération de correspondance de modèle avec un var_pattern réussit toujours.
Une var_pattern s’applique à chaque type.
var_pattern
: 'var' designation
;
designation
: simple_designation
| tuple_designation
;
tuple_designation
: '(' designations? ')'
;
designations
: designation (',' designation)*
;
Étant donné une valeur d’entrée de modèle (§11.1) e, si la désignation est discard_designation, elle désigne un abandon (§9.2.9.2) et la valeur e n’est liée à rien. (Bien qu’une variable déclarée portant ce nom puisse être dans l’étendue à ce stade, cette variable nommée n’est pas vue dans ce contexte.) Sinon, si la désignation est single_variable_designation, au moment de l’exécution, la valeur e est liée à une variable locale nouvellement introduite (§9.2.9) de ce nom dont le type est le type statique de e, et la valeur d’entrée du modèle est affectée à cette variable locale.
Il s’agit d’une erreur si le nom var est lié à un type où un var_pattern est utilisé.
Si la désignation est un tuple_designation, le modèle équivaut à un positional_pattern (§11.2.5) de la désignation de formulaire(var, ...
) où les désignationssont celles trouvées dans la tuple_designation. Par exemple, le modèle var (x, (y, z)) est équivalent à (var x, (var y, var z)).
11.2.5 Modèle positionnel
Un positional_pattern vérifie que la valeur d’entrée n’est pas null, appelle une méthode appropriée Deconstruct (§12.7) et effectue une correspondance de modèle supplémentaire sur les valeurs résultantes. Il prend également en charge une syntaxe de modèle de type tuple (sans le type fourni) lorsque le type de la valeur d’entrée est identique au type contenant Deconstruct, ou si le type de la valeur d’entrée est un type tuple, ou si le type de la valeur d’entrée est object ou System.ITuple si le type d’exécution de l’expression implémente System.ITuple.
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern (',' subpattern)*
;
subpattern
: pattern
| identifier ':' pattern
;
Étant donné une correspondance d’une valeur d’entrée auxsous-modèles) de type de modèle(, une méthode est sélectionnée en recherchant dans le type des déclarations accessibles et Deconstruct en sélectionnant une parmi elles à l’aide des mêmes règles que pour la déclaration de déconstruction.
Il s’agit d’une erreur si un positional_pattern omettre le type, a un sous-modèle unique sans identificateur, n’a aucun property_subpattern et n’a pas de simple_designation. Cela désambigue entre un constant_pattern entre parenthèses et un positional_pattern.
Pour extraire les valeurs à mettre en correspondance avec les modèles de la liste,
- Si le type est omis et que le type de l’expression d’entrée est un type tuple, le nombre de sous-modèles doit être identique à la cardinalité du tuple. Chaque élément tuple est mis en correspondance avec le sous-modèle correspondant, et la correspondance réussit si tous ces éléments réussissent. Si un sous-modèle a un identificateur, il doit nommer un élément tuple à la position correspondante dans le type tuple.
- Sinon, s’il existe un membre de type approprié
Deconstruct, il s’agit d’une erreur au moment de la compilation si le type de la valeur d’entrée n’est pas compatible avec le type. Au moment de l’exécution, la valeur d’entrée est testée par rapport au type. Si cela échoue, la correspondance de modèle positionnel échoue. Si elle réussit, la valeur d’entrée est convertie en ce type etDeconstructest appelée avec de nouvelles variables générées par le compilateur pour recevoir les paramètres de sortie. Chaque valeur reçue est mise en correspondance avec le sous-modèle correspondant, et la correspondance réussit si toutes ces valeurs réussissent. Si un sous-modèle a un identificateur, celui-ci doit nommer un paramètre à la position correspondante deDeconstruct. - Sinon, si le type est omis, et que la valeur d’entrée est de type
objectou d’un type qui peut être convertiSystem.ITuplepar une conversion de référence implicite, et qu’aucun identificateur n’apparaît parmi les sous-modèles, la correspondance utiliseSystem.ITuple. - Sinon, le modèle est une erreur au moment de la compilation.
L’ordre dans lequel les sous-modèles sont mis en correspondance au moment de l’exécution n’est pas spécifié et une correspondance ayant échoué peut ne pas tenter de faire correspondre tous les sous-modèles.
Exemple : ici, nous déconstructons un résultat d’expression et correspondons aux valeurs obtenues par rapport aux modèles imbriqués correspondants :
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); }exemple de fin
Exemple : Les noms des éléments tuple et des paramètres de déconstruction peuvent être utilisés dans un modèle positionnel, comme suit :
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); }La sortie produite est
Sum of [10 20 30] is 60exemple de fin
Modèle de propriété 11.2.6
Une property_pattern vérifie que la valeur d’entrée n’est pas null, et correspond de manière récursive aux valeurs extraites par l’utilisation de propriétés ou de champs accessibles.
property_pattern
: type? property_subpattern simple_designation?
;
property_subpattern
: '{' '}'
| '{' subpatterns ','? '}'
;
Il s’agit d’une erreur si un sous-modèle d’un property_pattern ne contient pas d’identificateur.
Il s’agit d’une erreur au moment de la compilation si le type est un type valeur nullable (§8.3.12) ou un type référence nullable (§8.9.3).
Remarque : Un modèle de vérification null ne fait pas partie d’un modèle de propriété trivial. Pour vérifier si la chaîne
sn’est pas null, il est possible d’écrire l’une des formes suivantes :#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 {}) ...Note de fin Étant donné une correspondance d’une expression e au type de modèle
{property_pattern_list}, il s’agit d’une erreur au moment de la compilation si l’expression e n’est pas compatible avec le type T désigné par type. Si le type est absent, le type est supposé être le type statique de e. Chacun des identificateurs apparaissant sur le côté gauche de son property_pattern_list doit désigner une propriété ou un champ accessible accessible en lecture de T. Si la simple_designation du property_pattern est présente, elle déclare une variable de modèle de type T.
Au moment de l’exécution, l’expression est testée sur T. Si cela échoue, la correspondance du modèle de propriété échoue et le résultat est false. Si elle réussit, chaque champ ou propriété property_subpattern est lu et sa valeur correspond à son modèle correspondant. Le résultat de la correspondance entière n’est false que si le résultat de l’un de ces résultats est false. L’ordre dans lequel les sous-modèles sont mis en correspondance n’est pas spécifié, et une correspondance ayant échoué peut ne pas tester tous les sous-modèles au moment de l’exécution. Si la correspondance réussit et que le simple_designation de l’property_pattern est un single_variable_designation, la variable déclarée est affectée à la valeur correspondante.
Le property_pattern peut être utilisé pour faire correspondre des modèles avec des types anonymes.
Exemple :
var o = ...; if (o is string { Length: 5 } s) ...exemple de fin
Exemple : une vérification de type d’exécution et une déclaration de variable peuvent être ajoutées à un modèle de propriété, comme suit :
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."), };La sortie produite est
Hello Hi! 12345 abcexemple de fin
11.2.7 Modèle d’abandon
Chaque expression correspond au modèle d’abandon, ce qui entraîne l’abandon de la valeur de l’expression.
discard_pattern
: '_'
;
Il s’agit d’une erreur au moment de la compilation d’utiliser un modèle d’abandon dans un relational_expression du modèle relational_expressionisformulaire ou comme modèle d’un switch_label.
Remarque : Dans ces cas, pour correspondre à n’importe quelle expression, utilisez un var_pattern avec un abandon
var _. Note de fin
Exemple :
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, };La sortie produite est
5.0 0.0 0.0Ici, un modèle d’abandon est utilisé pour gérer
nullet toute valeur entière qui n’a pas le membre correspondant de l’énumérationDayOfWeek. Cela garantit que l’expressionswitchgère toutes les valeurs d’entrée possibles. exemple de fin
11.3 Sous-énumération du modèle
Dans une instruction switch, il s’agit d’une erreur si le modèle d’un cas est subsumé par l’ensemble précédent de cas non surveillés (§13.8.3). De façon informelle, cela signifie que toute valeur d’entrée aurait été mise en correspondance par l’un des cas précédents. Les règles suivantes définissent lorsqu’un ensemble de modèles subsume un modèle donné :
Un modèle Pcorrespond à une constante K si la spécification du comportement d’exécution de ce modèle correspond à Pcelle-ci K .
Un ensemble de modèles Qsubsume un modèle P si l’une des conditions suivantes est en attente :
-
Pest un modèle constant et l’un des modèles de l’ensemble correspondraitQà la valeur convertie de l’ensembleP -
Pest un modèle var et l’ensemble de modèles est exhaustif (§11.4) pour le type de la valeur d’entrée du modèle (Q), et soit la valeur d’entrée du modèle n’est pas d’un type Nullable ou d’un modèle correspondant .Qnull -
Pest un modèle de déclaration avec typeTet l’ensemble de modèlesQest exhaustif pour le typeT(§11.4).
11.4 Exhaustive des modèles
De façon informelle, un ensemble de modèles est exhaustif pour un type si, pour chaque valeur possible de ce type autre que null, certains modèles de l’ensemble sont applicables. Les règles suivantes définissent lorsqu’un ensemble de modèles est exhaustif pour un type :
Un ensemble de modèles Q est exhaustif pour un type T si l’une des conditions suivantes est conservée :
-
Test un type intégral ou enum, ou une version nullable de l’un de ces éléments, et pour chaque valeur possible duTtype sous-jacent non nullable, un modèle dans lequelQil correspondrait à cette valeur ; ou - Certains modèles sont
Qun modèle var ; ou - Certains modèles sont
Qun modèle de déclaration pour le typeD, et il existe une conversion d’identité, une conversion de référence implicite ou une conversion de boxe àTpartir deD.
Exemple :
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; } }exemple de fin
ECMA C# draft specification