11 Criteri e criteri di ricerca

11.1 Generale

Un modello è una forma sintattica che può essere usata con l'operatore is (§12.15.12), in un switch_statement (§13.8.3) e in un switch_expression (§12.12) per esprimere la forma dei dati rispetto ai quali devono essere confrontati i dati in ingresso. I modelli possono essere ricorsivi, in modo che le parti dei dati possano essere confrontate con i modelli secondari.

Un criterio viene testato in base a un valore in diversi contesti:

  • In un'istruzione switch, il modello di un'etichetta case viene testato rispetto all'espressione dell'istruzione switch.
  • In un operatore is-pattern , il criterio sul lato destro viene testato sull'espressione a sinistra.
  • In un'espressione switch, il modello di un switch_expression_arm viene testato sull'espressione sul lato sinistro dell'espressione switch-expression.
  • Nei contesti annidati, il modello secondario viene testato in base ai valori recuperati da proprietà, campi o indicizzati da altri valori di input, a seconda del modulo del modello.

Il valore in base al quale viene testato un criterio è denominato valore di input del modello.

11.2 Moduli pattern

11.2.1 Generale

Un modello può avere una delle forme seguenti:

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 ')'
    ;

L'ambiente '(' pattern ')' di produzione consente di racchiudere un modello tra parentesi per applicare l'ordine di valutazione tra i modelli combinati usando uno dei logical_patterns.

Alcuni modellipossono comportare la dichiarazione di una variabile locale.

Ogni modulo modello definisce il set di tipi per i valori di input a cui può essere applicato il modello. Un criterio P è applicabile a un tipo T se T è tra i tipi i cui valori possono corrispondere al criterio. Si tratta di un errore in fase di compilazione se un criterio P viene visualizzato in un programma in modo che corrisponda a un valore di input del modello (§11.1) di tipo T se P non è applicabile a T.

Esempio: l'esempio seguente genera un errore in fase di compilazione perché il tipo in fase di compilazione di v è TextReader. Una variabile di tipo TextReader non può mai avere un valore compatibile con il riferimento con 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
}

Tuttavia, il codice seguente non genera un errore in fase di compilazione perché il tipo in fase di compilazione di v è object. Una variabile di tipo object può avere un valore compatibile con il riferimento con string:

object v = Console.In;
if (v is string s)
{
    // code assuming v is a string
}

esempio finale

Ogni modulo pattern definisce il set di valori per cui il criterio corrisponde al valore in fase di esecuzione.

L'ordine di valutazione delle operazioni e degli effetti collaterali durante la corrispondenza dei criteri (chiamate a , accessi alle Deconstructproprietà e chiamate di metodi in System.ITuple) non è specificato.

Modello di dichiarazione 11.2.2

Un declaration_pattern viene usato per verificare che un valore abbia un determinato tipo e, se il test ha esito positivo, per fornire facoltativamente il valore in una variabile di tale tipo.

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : discard_designation
    | single_variable_designation
    ;
discard_designation
    : '_'
    ;
single_variable_designation
    : identifier
    ;

Un simple_designation con il token _ deve essere considerato un discard_designation anziché un single_variable_designation.

Il tipo di runtime del valore viene testato sul tipo nel modello usando le stesse regole specificate nell'operatore is-type (§12.15.12.1). Se il test ha esito positivo, il criterio corrisponde a tale valore. Si tratta di un errore in fase di compilazione se il tipo è un tipo valore nullable (§8.3.12) o un tipo riferimento nullable (§8.9.3). Questo modulo di pattern non corrisponde mai a un null valore.

Nota: l'espressione e is T is-type e il modello e is T _ di dichiarazione sono equivalenti quando T non è un tipo nullable. nota finale

Dato un valore di input del criterio (§11.1) e, se il simple_designation è discard_designation, indica un'eliminazione (§9.2.9.2) e il valore di e non è associato ad alcun elemento. Anche se una variabile dichiarata con il nome _ può trovarsi nell'ambito in quel punto, tale variabile denominata non viene visualizzata in questo contesto. In caso contrario, se il simple_designation è single_variable_designation, viene introdotta una variabile locale (§9.2.9) del tipo specificato denominato dall'identificatore specificato. A tale variabile locale viene assegnato il valore del valore di input del pattern quando il criterio corrisponde al valore.

Alcune combinazioni di tipo statico del valore di input del modello e il tipo specificato sono considerate incompatibili e generano un errore in fase di compilazione. Si dice che un valore di tipo E statico sia compatibile con il tipo T se esiste una conversione identity, una conversione di riferimento implicita o esplicita, una conversione boxing, una conversione unboxing o una conversione implicita o esplicita di un tipo di valore nullable da E a To se ET è un tipo aperto (§8.4.3). Un modello di dichiarazione che denomina un tipo T è applicabile a.

Nota: il supporto per i tipi aperti può essere più utile quando si controllano i tipi che possono essere struct o tipi di classe ed è consigliabile evitare la conversione boxing. nota finale

Esempio: il modello di dichiarazione è utile per l'esecuzione di test di tipi di riferimento in fase di esecuzione e sostituisce il linguaggio

var v = expr as Type;
if (v != null) { /* code using v */ }

con il leggermente più conciso

if (expr is Type v) { /* code using v */ }

esempio finale

Esempio: il modello di dichiarazione può essere usato per testare i valori dei tipi nullable: un valore di tipo (o boxed Nullable<T>) corrisponde a un criterio T di tipo se il valore è diverso da null ed T2 id è T2oppure un tipo T o un'interfaccia di base di T. Ad esempio, nel frammento di codice

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

La condizione dell'istruzione if è true in fase di esecuzione e la variabile v contiene il valore 3 di tipo int all'interno del blocco. Dopo il blocco, la variabile v è nell'ambito, ma non è sicuramente assegnata. esempio finale

11.2.3 Modello costante

Un constant_pattern viene usato per testare il valore di un valore di input del modello (§11,1) rispetto al valore costante specificato.

constant_pattern
    : constant_expression
    ;

Un criterio P costante è applicabile a un tipo T se è presente una conversione implicita dall'espressione costante di P al tipo T.

Per un criterio Pcostante , il relativo valore convertito è

  • se il tipo del valore di input del criterio è un tipo integrale o un tipo enumerazione, il valore costante del criterio convertito in tale tipo; altrimenti
  • se il tipo del valore di input del criterio è la versione nullable di un tipo integrale o di un tipo enum, il valore costante del modello convertito nel tipo sottostante; altrimenti
  • valore del valore costante del criterio.

Dato un valore di input del criterio e un criterio P costante con valore convertito v,

  • se e ha tipo integrale o enumerazione o una forma nullable di uno di questi e v ha un tipo integrale, il criterio P corrisponde al valore e se il risultato dell'espressione e == v è true; in caso contrario,
  • il criterio Pcorrisponde al valore e se object.Equals(e, v) restituisce true.

Esempio: l'istruzione switch nel metodo seguente usa cinque modelli costanti nelle etichette case.

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

esempio finale

11.2.4 Modello var

Un var_patterncorrisponde a ogni valore. Ovvero, un'operazione di corrispondenza dei criteri con un var_pattern ha sempre esito positivo.

Un var_pattern è applicabile a ogni tipo.

var_pattern
    : 'var' designation
    ;
designation
    : simple_designation
    | tuple_designation
    ;
tuple_designation
    : '(' designations? ')'
    ;
designations
    : designation (',' designation)*
    ;

Dato un valore di input del criterio (§11.1) e, se la designazione è discard_designation, indica un'eliminazione (§9.2.9.2) e il valore di e non è associato ad alcun elemento. Sebbene una variabile dichiarata con tale nome possa trovarsi nell'ambito in quel punto, tale variabile denominata non viene visualizzata in questo contesto. In caso contrario, se la designazione è single_variable_designation, in fase di esecuzione il valore di e è associato a una variabile locale appena introdotta (§9.2.9) di tale nome il cui tipo è il tipo statico di e e il valore di input del modello viene assegnato a tale variabile locale.

Si tratta di un errore se il nome var viene associato a un tipo in cui viene usato un var_pattern .

Se la designazione è un tuple_designation, il modello equivale a un positional_pattern (§11.2.5) della (var del modulo, ... ) dove la designaziones sono quelle presenti all'interno del tuple_designation. Ad esempio, il modello var (x, (y, z)) equivale a (var x, (var y, var z)).

11.2.5 Modello posizionale

Un positional_pattern verifica che il valore di input non nullsia , richiama un metodo appropriato Deconstruct (§12.7) ed esegue ulteriori criteri di ricerca sui valori risultanti. Supporta inoltre una sintassi del modello simile a una tupla (senza il tipo specificato) quando il tipo del valore di input è uguale al tipo contenente Deconstructo se il tipo del valore di input è un tipo di tupla oppure se il tipo del valore di input è object o System.ITuple e il tipo di runtime dell'espressione implementa System.ITuple.

positional_pattern
    : type? '(' subpatterns? ')' property_subpattern? simple_designation?
    ;
subpatterns
    : subpattern (',' subpattern)*
    ;
subpattern
    : pattern
    | identifier ':' pattern
    ;

Dato una corrispondenza di un valore di input aisottopattern di(tipo) modello, un metodo viene selezionato eseguendo una ricerca nel tipo per le dichiarazioni accessibili di Deconstruct e selezionandone una usando le stesse regole della dichiarazione di decostruzione. Si tratta di un errore se un positional_pattern omette il tipo, ha un singolo sottopattern senza identificatore, non ha property_subpattern e non ha simple_designation. Questo disambigua tra un constant_pattern racchiuso tra parentesi e un positional_pattern. Per estrarre i valori da trovare in base ai modelli nell'elenco,

  • Se il tipo viene omesso e il tipo dell'espressione di input è un tipo di tupla, il numero di sottopattern deve corrispondere alla cardinalità della tupla. Ogni elemento della tupla viene confrontato con il corrispondente sottopattern e la corrispondenza ha esito positivo se tutte queste operazioni hanno esito positivo. Se uno dei sottopattern ha un identificatore, verrà assegnato un nome a un elemento tupla nella posizione corrispondente nel tipo di tupla.
  • In caso contrario, se esiste un elemento appropriato Deconstruct come membro di tipo, si tratta di un errore in fase di compilazione se il tipo del valore di input non è compatibile con il modello con il tipo. In fase di esecuzione il valore di input viene testato sul tipo. Se l'operazione ha esito negativo, la corrispondenza del criterio posizionale ha esito negativo. Se ha esito positivo, il valore di input viene convertito in questo tipo e Deconstruct viene richiamato con variabili nuove generate dal compilatore per ricevere i parametri di output. Ogni valore ricevuto viene confrontato con il corrispondente sottopattern e la corrispondenza ha esito positivo se tutte queste operazioni hanno esito positivo. Se un subpattern ha un identificatore, tale parametro deve assegnare un nome alla posizione corrispondente di Deconstruct.
  • In caso contrario, se il tipo viene omesso e il valore di input è di tipo object o di un tipo che può essere convertito System.ITuple da una conversione di riferimento implicita e non viene visualizzato alcun identificatore tra i sottopattern, la corrispondenza usa System.ITuple.
  • In caso contrario, il modello è un errore in fase di compilazione.

L'ordine in cui i sottopattern vengono confrontati in fase di esecuzione non è specificato e una corrispondenza non riuscita potrebbe non tentare di trovare una corrispondenza con tutti i sottopattern.

Esempio: in questo caso, viene distrutto un risultato di un'espressione e vengono confrontati i valori risultanti rispetto ai modelli annidati corrispondenti:

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

esempio finale

Esempio: i nomi degli elementi della tupla e dei parametri deconstruct possono essere usati in un modello posizionale, come indicato di seguito:

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

L'output prodotto è

Sum of [10 20 30] is 60

esempio finale

Modello di proprietà 11.2.6

Un property_pattern verifica che il valore di input non nullsia e corrisponda in modo ricorsivo ai valori estratti dall'uso di proprietà o campi accessibili.

property_pattern
    : type? property_subpattern simple_designation?
    ;
property_subpattern
    : '{' '}'
    | '{' subpatterns ','? '}'
    ;

Si tratta di un errore se un sottopattern di un property_pattern non contiene un identificatore.

Si tratta di un errore in fase di compilazione se il tipo è un tipo valore nullable (§8.3.12) o un tipo riferimento nullable (§8.9.3).

Nota: un modello di controllo null non rientra in un modello di proprietà semplice. Per verificare se la stringa s è non Null, è possibile scrivere uno dei formati seguenti:

#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 {}) ...

nota finale

Data una corrispondenza di un'espressione e aisottopattern di{tipo} pattern, si tratta di un errore in fase di compilazione se l'espressione e non è compatibile con il tipo T designato dal tipo. Se il tipo è assente, si presuppone che il tipo sia il tipo statico di e. Ognuno degli identificatori visualizzati sul lato sinistro dei relativi sottopattern deve designare una proprietà o un campo leggibile accessibile di T. Se la simple_designation del property_pattern è presente, dichiara una variabile di modello di tipo T.

In fase di esecuzione, l'espressione viene testata su T. Se l'errore ha esito negativo, la corrispondenza del criterio di proprietà ha esito negativo e il risultato è false. Se ha esito positivo, ogni property_subpattern campo o proprietà viene letto e il relativo valore corrisponde al modello corrispondente. Il risultato dell'intera corrispondenza è false solo se il risultato di uno di questi è false. L'ordine in cui i sottopattern vengono confrontati non è specificato e una corrispondenza non riuscita potrebbe non testare tutti i sottopattern in fase di esecuzione. Se la corrispondenza ha esito positivo e il simple_designation del property_pattern è un single_variable_designation, alla variabile dichiarata viene assegnato il valore corrispondente.

Il property_pattern può essere usato per trovare una corrispondenza tra criteri e tipi anonimi.

Esempio:

var o = ...;
if (o is string { Length: 5 } s) ...

esempio finale

Esempio: un controllo del tipo di runtime e una dichiarazione di variabile possono essere aggiunti a un modello di proprietà, come indicato di seguito:

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

L'output prodotto è

Hello
Hi!
12345
abc

esempio finale

11.2.7 Modello di eliminazione

Ogni espressione corrisponde al criterio di eliminazione, che determina l'eliminazione del valore dell'espressione.

discard_pattern
    : '_'
    ;

Si tratta di un errore in fase di compilazione per usare un modello di eliminazione in un relational_expression del modulo relational_expressionismodello o come modello di un switch_label.

Nota: in questi casi, per trovare una corrispondenza con qualsiasi espressione, usare un var_pattern con un'eccezione discard var _. nota finale

Esempio:

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

L'output prodotto è

5.0
0.0
0.0

In questo caso, viene usato un criterio di eliminazione per gestire null e qualsiasi valore intero che non dispone del membro corrispondente dell'enumerazione DayOfWeek . Ciò garantisce che l'espressione switch gestisca tutti i possibili valori di input. esempio finale

Modello di tipo 11.2.8

Un type_pattern viene usato per verificare che il valore di input del modello (§11.1) abbia un determinato tipo.

type_pattern
    : type
    ;

Un modello di tipo che denomina un tipo è applicabile a ogni tipo ET per il quale E è compatibile con T il modello (§11.2.2).

Il tipo di runtime del valore viene testato in base al tipo utilizzando le stesse regole specificate nell'operatore di tipo is (§12.15.12.1). Se il test ha esito positivo, il criterio corrisponde a tale valore. Si tratta di un errore in fase di compilazione se il tipo è un tipo nullable. Questo modulo di pattern non corrisponde mai a un null valore.

Modello relazionale 11.2.9

Un relational_pattern viene usato per testare in modo relazionale il valore di input del modello (§11,1) rispetto a un valore costante.

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

Il relational_expression in un relational_pattern è necessario per restituire un valore costante.

I modelli relazionali supportano gli operatori <relazionali , ><=, e >= in tutti i tipi predefiniti che supportano tali operatori relazionali binari con entrambi gli operandi con lo stesso tipo: sbyte, ushortshortintbyte, uint, nintlongcharnuintulongfloatdoubledecimal, e enumerazioni.

Un relational_pattern è applicabile a un tipo T se un operatore relazionale binario predefinito appropriato è definito con entrambi gli operandi di tipo To se esiste una conversione esplicita nullable o unboxing da T al tipo dell'espressione costante.

Si tratta di un errore in fase di compilazione se l'espressione restituisce double.NaN, float.NaNo una costante Null.

Quando il valore di input ha un tipo per il quale viene definito un operatore relazionale binario predefinito appropriato, la valutazione di tale operatore viene presa come significato del modello relazionale. In caso contrario, il valore di input viene convertito nel tipo dell'espressione costante usando una conversione esplicita nullable o unboxing. Si tratta di un errore in fase di compilazione se non esiste alcuna conversione di questo tipo. Il modello viene considerato non corrispondente se la conversione non riesce. Se la conversione ha esito positivo, il risultato dell'operazione di corrispondenza dei criteri è il risultato della valutazione dell'espressione e «op» v in cui e è l'input convertito, «op» è l'operatore relazionale e v è l'espressione costante.

Esempio:

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

L'output prodotto è

Too high
Unknown
Acceptable

esempio finale

11.2.10 Modello logico

Un logical_pattern viene usato per negare il risultato di una corrispondenza del criterio o per combinare i risultati di più corrispondenze di criteri usando la congiunzione (and) o la disgiunzione (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, ande or sono denominati collettivamente operatori pattern.

Un negated_pattern corrisponde se il criterio negato non corrisponde e viceversa. Un conjunctive_pattern richiede che entrambi i modelli corrispondano. Un disjunctive_pattern richiede che uno dei due criteri corrisponda. A differenza delle controparti dell'operatore && di linguaggio e ||, and e ornon sono operatori a corto circuito.

Si tratta di un errore in fase di compilazione per dichiarare una variabile di modello sotto un not operatore o or pattern.

Nota: poiché né notor né può produrre un'assegnazione definita per una variabile di modello, è un errore dichiararne uno in tali posizioni. nota finale

In un conjunctive_pattern, il tipo di input del secondo criterio viene ristretto in base ai requisiti di restringimento del tipo del primo modello di and. Il tipo ristretto di un criterio P è definito come segue:

  • Se P è un modello di tipo, il tipo ristretto è il tipo del tipo del modello di tipo.
  • In caso contrario, se P è un modello di dichiarazione, il tipo ristretto è il tipo del tipo del modello di dichiarazione.
  • In caso contrario, se P è un modello ricorsivo che fornisce un tipo esplicito, il tipo ristretto è quel tipo.
  • In caso contrario, se P viene trovata una corrispondenza tramite le regole per ITuple in un positional_pattern (§11.2.5), il tipo ristretto è il tipo System.ITuple.
  • In caso contrario, se P è un criterio costante in cui la costante non è la costante Null e dove l'espressione non ha alcuna conversione costante di espressioni nel tipo di input, il tipo ristretto è il tipo della costante.
  • In caso contrario, se P è un modello relazionale in cui l'espressione costante non ha alcuna conversione di espressioni costanti nel tipo di input, il tipo ristretto è il tipo della costante.
  • In caso contrario, se P è un or criterio, il tipo ristretto è il tipo comune del tipo ristretto dei sottopattern se esiste un tipo così comune. A questo scopo, l'algoritmo di tipo comune considera solo le conversioni di identità, boxing e riferimenti impliciti e considera tutte le sottopatterne di una sequenza di or modelli (ignorando i modelli racchiusi tra parentesi).
  • In caso contrario, se P è un and modello, il tipo ristretto è il tipo ristretto del modello a destra. Inoltre, il tipo ristretto del pattern sinistro è il tipo di input del modello a destra.
  • In caso contrario, il tipo ristretto di è Pil tipo di P input.

Nota: come indicato dalla grammatica, not ha la precedenza su and, che ha la precedenza su or. È possibile indicare o eseguire l'override in modo esplicito usando le parentesi. nota finale

Quando un criterio viene visualizzato sul lato destro di is, l'estensione del modello è determinata dalla grammatica. Di conseguenza, gli operatori anddi pattern , ore not all'interno del criterio si associano più strettamente rispetto agli operatori &&logici , ||e ! all'esterno del modello.

Esempio:

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

L'output prodotto è

High
Too low
Acceptable

esempio finale

Esempio:

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

L'output prodotto è

winter
autumn
spring

esempio finale

Esempio:

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

L'output prodotto è

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

esempio finale

11.3 Subsumption pattern

In un'istruzione switch si verifica un errore se il modello di un case è sottosumato dal set precedente di case non sorvegliati (§13.8.3). In modo informale, ciò significa che qualsiasi valore di input sarebbe stato confrontato con uno dei casi precedenti. Le regole seguenti definiscono quando un set di modelli esegue il subsume di un determinato modello:

Un criterio Pcorrisponde a una costante K se una delle condizioni seguenti contiene:

  • la specifica per il comportamento di runtime del modello corrisponde P a K.
  • P è un type_pattern per il tipo T e K non null è e il tipo di runtime di K è T o un tipo derivato da T o un tipo che implementa T.
  • P è un relational_pattern con l'operatore «op» e la costante ve l'espressione K «op» v restituisce true.
  • P è un negated_patternnot P₁ e P₁ non corrisponde Ka .
  • P è un conjunctive_patternP₁ and P₂ e entrambi P₁ corrisponderebbero K e P₂ corrisponderebbero Ka .
  • P è un disjunctive_patternP₁ or P₂ e P₁ corrisponde K o P₂ corrisponde Ka .
  • P è un discard_pattern.

Un set di modelli Qesegue il subsume di un modello P se una delle condizioni seguenti contiene:

  • P è un modello costante e uno dei modelli nel set Q corrisponderebbe Pal valore convertito
  • P è un criterio var e il set di modelli Q è esaustivo (§11.4) per il tipo del valore di input del pattern (§11.1) e il valore di input del criterio non è di un tipo nullable o un criterio in Q corrisponde nulla .
  • P è un modello di dichiarazione con tipo T e il set di modelli Q è esaustivo per il tipo T (§11.4).
  • P è un type_pattern per il tipo T e il set di modelli Q è esaustivo per il tipo T.
  • P è un relational_pattern con l'operatore «op» e il valore vcostante e per ogni valore del tipo di input che soddisfa la relazione «op» v, alcuni criteri nel set Q corrispondono a tale valore.
  • P è un disjunctive_patternP₁ or P₂ e il set di Q sottosumes P₁ e Q subsumes P₂.
  • P è un conjunctive_patternP₁ and P₂ e almeno uno dei blocchi seguenti: Q subsumes P₁o Q subsumes P₂.
  • P è un negated_patternnot P₁ ed Q è esaustivo per il tipo di input considerando solo i valori non corrispondenti a P₁.
  • P è un discard_pattern e il set di modelli Q è esaustivo per il tipo del valore di input del pattern e il valore di input del pattern non è di un tipo nullable o un criterio in Q corrisponde nulla .
  • Un modello in Q è un disjunctive_patternQ₁ or Q₂ e la sostituzione di tale modello con Q₁ in Q restituirà un set che esegue il sottosume Po la sostituzione con Q₂ restituirà un set che restituisce un sottosume P.
  • Alcuni criteri in Q sono un negated_patternnot Q₁ e P non corrispondono ad alcun valore corrispondente Q₁ .

11.4 Esaustività dei criteri

In modo informale, un set di modelli è esaustivo per un tipo se, per ogni valore possibile di tale tipo diverso da Null, è applicabile un modello nel set. Le regole seguenti definiscono quando un set di modelli è esaustivo per un tipo:

Un set di modelli Q è esaustivo per un tipo T se una delle condizioni seguenti contiene:

  1. T è un tipo integrale o enumerazione o una versione nullable di uno di questi e per ogni valore possibile del Ttipo sottostante non nullable, alcuni criteri in Q corrisponderebbero a tale valore; o
  2. Un modello in Q è un modello var; o
  3. Alcuni criteri in Q sono un modello di dichiarazione per il tipo De esiste una conversione di identità, una conversione di riferimento implicita o una conversione boxing da T a D; o
  4. Alcuni criteri in Q sono un type_pattern per il tipo De esiste una conversione di identità, una conversione di riferimento implicita o una conversione boxing da T a D; o
  5. Un modello in Q è un discard_pattern; o
  6. I modelli in Q includono una combinazione di relational_patterne constant_patterns i cui intervalli coprono collettivamente ogni valore possibile del Ttipo sottostante non nullable. Per float i tipi e double include System.Double.NaN o System.Single.NaN , rispettivamente, poiché NaN non corrisponde a alcun modello relazionale; o
  7. Un modello in Q è un disjunctive_patternP₁ or P₂ e la sostituzione di tale modello con P₁ e P₂ in Qrestituisce un set esaustivo per T; o
  8. Alcuni criteri in Q sono un negated_patternnot P₁ e i modelli in Q insieme ai valori non corrispondono per P₁ coprire ogni valore possibile di T. Un negated_patternnot P₁ è esaustivo da solo quando P₁ non corrisponde a alcun valore possibile di T; o
  9. Alcuni criteri in Q sono un conjunctive_patternP₁ and P₂ e il set contenente solo P₁ è esaustivo per T e il set contenente solo P₂ è esaustivo per T.

Nota: quando un criterio di tipo include tipi nullable, il modello può essere esaustivo per il tipo ma genera comunque un avviso perché il criterio di tipo non corrisponde a un null valore. nota finale

Nota: per i tipi a virgola mobile, la combinazione di modelli < 0 e >= 0non è esaustiva perché nessuno dei modelli relazionali corrisponde a NaN. Un set completo corretto sarà < 0, >= 0e double.NaN (o float.NaN). nota finale

Esempio:

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

esempio finale