11 Modèles et correspondances de modèles

11.1 Général

Un modèle est une forme syntactique qui peut être utilisée avec l’opérateur is (§12.15.12), dans un switch_statement (§13.8.3) et dans un switch_expression (§12.12) 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
    : logical_pattern
    ;

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

parenthesized_pattern
    : '(' pattern ')'
    ;

La '(' pattern ')' production permet à un modèle d’être placé entre parenthèses pour appliquer l’ordre d’évaluation entre les modèles combinés à l’aide de l’un des logical_patterns.

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 type TextReader ne peut jamais avoir de valeur compatible référence avec 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
}

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 type object peut avoir une valeur compatible référence avec string:

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.15.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 T is-type et le modèle e is T _ de déclaration sont équivalents lorsqu’il T n’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 un modèle 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, une conversion unboxing ou une conversion implicite ou explicite de type valeur nullable de E vers T, ou si ET 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 zone T) correspond à un modèle T2 id de type si la valeur est non null et T2 est T, ou un type de base ou une interface de T. Par exemple, dans le fragment de code

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

La condition de l’instruction est if au moment de l’exécution true et la variable v contient la valeur 3 du type int à l’intérieur du bloc. Une fois que le bloc de la variable v est 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’expression e == v est true; sinon,
  • le modèle Pcorrespond à la valeur e si object.Equals(e, v) elle est retournée true.

Exemple : L’instruction switch de 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 (var de formulaire, ... ) 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 Deconstruct approprié, 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 et Deconstruct est 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 de Deconstruct.
  • Sinon, si le type est omis, et que la valeur d’entrée est de type object ou d’un type qui peut être converti System.ITuple par une conversion de référence implicite, et qu’aucun identificateur n’apparaît parmi les sous-modèles, la correspondance utilise System.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 60

exemple 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 s n’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 auxsous-modèles{ de type de modèle}, 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 ses sous-modèles 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
abc

exemple 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.0

Ici, un modèle d’abandon est utilisé pour gérer null et toute valeur entière qui n’a pas le membre correspondant de l’énumération DayOfWeek . Cela garantit que l’expression switch gère toutes les valeurs d’entrée possibles. exemple de fin

Modèle de type 11.2.8

Une type_pattern est utilisée pour tester que la valeur d’entrée du modèle (§11.1) a un type donné.

type_pattern
    : type
    ;

Un modèle de type nommant un type Ts’applique à chaque type E pour lequel E il est compatible avec T (§11.2.2).

Le type d’exécution de la valeur est testé par rapport au type à l’aide des mêmes règles spécifiées dans l’opérateur is-type (§12.15.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 Nullable. Ce formulaire de modèle ne correspond jamais à une null valeur.

11.2.9 Modèle relationnel

Une relational_pattern est utilisée pour tester relationnellement la valeur d’entrée du modèle (§11.1) par rapport à une valeur constante.

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

La relational_expression dans un relational_pattern est nécessaire pour évaluer une valeur constante.

Les modèles relationnels prennent en charge les opérateurs <relationnels , ><=et >= sur tous les types intégrés qui prennent en charge ces opérateurs relationnels binaires avec les deux opérandes ayant le même type : sbyte, , byteshort, ushort, , , int, uint, floatnintulongnuintlongchardoubledecimalet enums.

Une relational_patterns’applique à un type T si un opérateur relationnel binaire intégré approprié est défini avec les deux opérandes de type T, ou si une conversion explicite nullable ou unboxing existe depuis T le type de l’expression constante.

Il s’agit d’une erreur au moment de la compilation si l’expression prend double.NaNla valeur , float.NaNou une constante Null.

Lorsque la valeur d’entrée a un type pour lequel un opérateur relationnel binaire intégré approprié est défini, l’évaluation de cet opérateur est considérée comme la signification du modèle relationnel. Sinon, la valeur d’entrée est convertie en type d’expression constante à l’aide d’une conversion explicite nullable ou unboxing. Il s’agit d’une erreur de compilation si aucune conversion de ce type n’existe. Le modèle est considéré comme ne correspondant pas si la conversion échoue. Si la conversion réussit, le résultat de l’opération de correspondance de modèle est le résultat de l’évaluation de l’expression e «op» ve est l’entrée convertie, « op » est l’opérateur relationnel et v est l’expression constante.

Exemple :

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",
};

La sortie produite est

Too high
Unknown
Acceptable

exemple de fin

Modèle logique 11.2.10

Une logical_pattern est utilisée pour négation du résultat d’une correspondance de modèle, ou pour combiner les résultats de plusieurs correspondances de modèle à l’aide de la conjonction (and) ou de la disjonction (or).

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, andet or sont collectivement appelés opérateurs de modèle.

Une negated_pattern correspond si le modèle en cours de négation ne correspond pas, et vice versa. Une conjunctive_pattern nécessite que les deux modèles correspondent. Une disjunctive_pattern nécessite que l’un ou l’autre modèle corresponde. Contrairement à leurs équivalents d’opérateur de langue et && , and et ||orne sont pas des opérateurs de court-circuit.

Il s’agit d’une erreur au moment de la compilation pour qu’une variable de modèle soit déclarée sous un not ou or un opérateur de modèle.

Remarque : Étant donné que ni or aucune not affectation ne peut produire d’affectation définie pour une variable de modèle, il s’agit d’une erreur de déclarer celle-ci dans ces positions. Note de fin

Dans un conjunctive_pattern, le type d’entrée du deuxième modèle est réduit par les exigences de réduction de type du premier modèle du and. Le type restreint d’un modèle P est défini comme suit :

  • S’il P s’agit d’un modèle de type, le type étroit est le type du modèle de type.
  • Sinon, s’il s’agit P d’un modèle de déclaration, le type restreint est le type du modèle de déclaration.
  • Sinon, s’il s’agit P d’un modèle récursif qui donne un type explicite, le type étroit est ce type.
  • Sinon, si P elle est mise en correspondance via les règles d’un ITuplepositional_pattern (§11.2.5), le type étroit est le type System.ITuple.
  • Sinon, s’il s’agit P d’un modèle constant où la constante n’est pas la constante Null et où l’expression n’a pas de conversion d’expression constante vers le type d’entrée, le type restreint est le type de la constante.
  • Sinon, s’il s’agit P d’un modèle relationnel où l’expression constante n’a pas de conversion d’expression constante vers le type d’entrée, le type restreint est le type de la constante.
  • Sinon, s’il s’agit P d’un or modèle, le type étroit est le type commun du type étroit des sous-modèles s’il existe un type commun. À cet effet, l’algorithme de type commun considère uniquement les conversions d’identité, de boxe et de référence implicite, et considère tous les sous-modèles d’une séquence de or modèles (ignorant les modèles entre parenthèses).
  • Sinon, s’il s’agit P d’un and modèle, le type étroit est le type étroit du motif droit. De plus, le type étroit du modèle gauche est le type d’entrée du modèle droit.
  • Sinon, le type restreint de P type d’entrée est P« type d’entrée ».

Remarque : Comme indiqué par la grammaire, not a la priorité sur and, qui a la priorité sur or. Cela peut être explicitement indiqué ou substitué à l’aide de parenthèses. Note de fin

Lorsqu’un modèle apparaît sur le côté droit du ismodèle, l’étendue du modèle est déterminée par la grammaire ; par conséquent, les opérateurs andde modèle , oret not dans le modèle se lient plus étroitement que les opérateurs logiques &&, ||et ! en dehors du modèle.

Exemple :

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",
};

La sortie produite est

High
Too low
Acceptable

exemple de fin

Exemple :

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}."),
};

La sortie produite est

winter
autumn
spring

exemple de fin

Exemple :

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}");

La sortie produite est

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

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 l’une des conditions suivantes est en attente :

  • la spécification du comportement d’exécution de ce modèle correspond PKà .
  • P est une type_pattern pour le type T et K n’est pas null et le type d’exécution d’est KT ou d’un type dérivé ou T d’un type qui implémente T.
  • P est une relational_pattern avec l’opérateur « op » et la constante v, et l’expression K « op » v serait évaluée à true.
  • P est une negated_patternnot P₁ et P₁ ne correspond Kpas .
  • P est une conjunctive_patternP₁ and P₂ et les deux P₁ correspondraient K et P₂ correspondraient K.
  • P est une disjunctive_patternP₁ or P₂ et P₁ correspondrait K ou P₂ correspondrait K.
  • P est une discard_pattern.

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 correspondrait Pà la valeur convertie de l’ensemble Q
  • 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
  • P est un modèle de déclaration avec type T et l’ensemble de modèles Q est exhaustif pour le type T (§11.4).
  • P est une type_pattern pour le type T et l’ensemble de modèles Q est exhaustif pour le type T.
  • P est une relational_pattern avec l’opérateur « op » et la valeur vconstante , et pour chaque valeur du type d’entrée répondant à la relation « op », vcertains modèles de l’ensemble Q correspondent à cette valeur.
  • Pest un disjunctive_patternP₁ or P₂ et l’ensemble de sous-énumérations et Q de sous-ensembles QP₁P₂de modèles .
  • P est une conjunctive_patternP₁ and P₂ et au moins l’une des conservations suivantes : Q sous-sommes P₁, ou Q sous-sommes P₂.
  • P est une negated_patternnot P₁ et Q est exhaustive pour le type d’entrée considérant uniquement les valeurs non mises en correspondance par P₁.
  • P est une discard_pattern et l’ensemble de modèles Q est exhaustif pour le type de la valeur d’entrée du modèle, et la valeur d’entrée du modèle n’est pas d’un type nullable ou un modèle dans lequel correspondrait Qnull.
  • Certains modèles dans Q sont un disjunctive_patternQ₁ or Q₂ et le remplacement de ce modèle par Q₁ un Q ensemble qui génère un ensemble qui sous-énumére P, ou le remplacer Q₂ par générerait un ensemble qui sous-énumére P.
  • Certains modèles sont Q un negated_patternnot Q₁ et P ne correspondent pas à une valeur qui Q₁ correspond.

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 :

  1. T est un type intégral ou enum, ou une version nullable de l’un de ces éléments, et pour chaque valeur possible du Ttype sous-jacent non nullable, un modèle dans lequel Q il correspondrait à cette valeur ; ou
  2. Certains modèles sont Q un modèle var ; ou
  3. Un modèle de Q déclaration est un 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 à Dpartir de T ; ou
  4. Certains modèles sont Q un type_pattern pour le type D, et il existe une conversion d’identité, une conversion de référence implicite ou une conversion de boxe à partir de T ; Dou
  5. Certains modèles sont Q un discard_pattern ; ou
  6. Les modèles inclus Q incluent une combinaison de relational_patterns et de constant_patterndont les plages couvrent collectivement toutes les valeurs possibles du type sous-jacent non Nullable.T Pour float et double les types, cela inclut System.Double.NaN ou System.Single.NaN respectivement, car NaN n’est mis en correspondance par aucun modèle relationnel ; ou
  7. Certains modèles dans sont Q un disjunctive_patternP₁ or P₂, et le remplacement de ce modèle par les deux P₁ et P₂ dans Q un ensemble qui est exhaustif pour T; ou
  8. Certains modèles dans sont Q un negated_patternnot P₁, et les modèles en même temps que les valeurs non mises en Q correspondance par P₁ toutes les valeurs possibles de T. Une negated_patternnot P₁ est exhaustive par elle-même lorsqu’elle P₁ ne correspond à aucune valeur possible de T; ou
  9. Certains modèles dans sont Q un conjunctive_patternP₁ and P₂, et l’ensemble contenant uniquement P₁ est exhaustif pour T et l’ensemble contenant uniquement P₂ est exhaustif pour T.

Remarque : Lorsqu’un modèle de type inclut des types nullables, le modèle peut être exhaustif pour le type, mais génère toujours un avertissement, car le modèle de type ne correspond pas à une null valeur. Note de fin

Remarque : Pour les types à virgule flottante, la combinaison de modèles < 0 et >= 0n’est pas exhaustive, car aucun modèle relationnel ne correspond NaN. Un ensemble exhaustif correct serait < 0, >= 0et double.NaN (ou float.NaN). Note de fin

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