Criteri di ricerca: le espressioni is e switch, gli operatori andor e not nei criteri

Usare l'espressione is, l'istruzione switch e l'espressione switch per trovare una corrispondenza con un'espressione di input rispetto a un numero qualsiasi di caratteristiche. C# supporta più criteri, tra cui dichiarazione, tipo, costante, relazionale, proprietà, elenco, var e discard. I criteri possono essere combinati usando parole chiave della logica booleana and, or e not.

Le espressioni e le istruzioni di C# seguenti supportano i criteri di ricerca:

In questi costrutti, è possibile associare un'espressione di input a uno dei criteri seguenti:

  • Criterio di dichiarazione: per controllare il tipo di runtime di un'espressione e, se è presente una corrispondenza, assegnare un risultato dell'espressione a una variabile dichiarata.
  • Tipo di criterio: per controllare il tipo di runtime di un'espressione.
  • Criterio costante: per verificare se un risultato di espressione è uguale a una costante specificata.
  • Criteri relazionali: per confrontare un risultato di espressione con una costante specificata.
  • Criteri logici: per verificare se un'espressione corrisponde a una combinazione logica di criteri.
  • Criterio di proprietà: per verificare se le proprietà o i campi di un'espressione corrispondono a modelli annidati.
  • Criterio posizionale: per decostruire un risultato dell'espressione e verificare se i valori risultanti corrispondono a modelli annidati.
  • Criterio var: per trovare una corrispondenza con qualsiasi espressione e assegnarne il risultato a una variabile dichiarata.
  • Criterio discard: per trovare la corrispondenza con qualsiasi espressione.
  • Criteri elenco: per verificare se gli elementi della sequenza corrispondono ai modelli annidati corrispondenti. Introdotto in C# 11.

I criteri logico, proprietà, posizionale ed elenco sono criteri di ricorsivi. Ovvero, possono contenere criteri annidati.

Per un esempio su come usare questi criteri per creare un algoritmo basato sui dati, vedere Esercitazione: usare criteri di ricerca per creare algoritmi basati sui tipi e sui dati.

Criteri di dichiarazione e di tipo

È possibile usare modelli di dichiarazione e di tipo per verificare se il tipo di runtime di un'espressione è compatibile con un determinato tipo. Con un criterio di dichiarazione, è anche possibile dichiarare una nuova variabile locale. Quando un criterio di dichiarazione corrisponde a un'espressione, a tale variabile viene assegnato un risultato di espressione convertita, come illustrato nell'esempio seguente:

object greeting = "Hello, World!";
if (greeting is string message)
{
    Console.WriteLine(message.ToLower());  // output: hello, world!
}

Un criterio di dichiarazione con tipo T corrisponde a un'espressione quando un risultato di un'espressione è diverso da Null e una delle condizioni seguenti è vera:

  • Il tipo di runtime di un risultato dell'espressione è T.

  • Il tipo di runtime di un risultato di un'espressione deriva dal tipo T, implementa l'interfaccia T o esiste un'altra conversione di riferimento implicita da essa a T. L'esempio seguente illustra due casi in cui questa condizione è True:

    var numbers = new int[] { 10, 20, 30 };
    Console.WriteLine(GetSourceLabel(numbers));  // output: 1
    
    var letters = new List<char> { 'a', 'b', 'c', 'd' };
    Console.WriteLine(GetSourceLabel(letters));  // output: 2
    
    static int GetSourceLabel<T>(IEnumerable<T> source) => source switch
    {
        Array array => 1,
        ICollection<T> collection => 2,
        _ => 3,
    };
    

    Nell'esempio precedente, alla prima chiamata al GetSourceLabel metodo, il primo criterio corrisponde a un valore di argomento perché il tipo di runtime dell'argomento int[] deriva dal tipo Array. Nella seconda chiamata al metodo GetSourceLabel, il tipo di runtime dell'argomento List<T> non deriva dal tipo Array ma implementa l'interfaccia ICollection<T>.

  • Il tipo di runtime del risultato di un'espressione è un tipo valore nullable con il tipo sottostante T.

  • Esiste una conversione boxing o unboxing dal tipo di run-time del risultato di un'espressione al tipo T.

L'esempio seguente illustra le ultime due condizioni:

int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
    Console.WriteLine(a + b);  // output: 30
}

Se si vuole controllare solo il tipo di un'espressione, occorre usare un'istruzione discard _ al posto del nome di una variabile, come illustrato nell'esempio seguente:

public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}

public static class TollCalculator
{
    public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
    {
        Car _ => 2.00m,
        Truck _ => 7.50m,
        null => throw new ArgumentNullException(nameof(vehicle)),
        _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
    };
}

A tale scopo, è possibile usare un criterio di tipo, come illustrato nell'esempio seguente:

public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
    Car => 2.00m,
    Truck => 7.50m,
    null => throw new ArgumentNullException(nameof(vehicle)),
    _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};

Analogamente a un criterio di dichiarazione, un criterio di tipo corrisponde a un'espressione quando il risultato di espressione è diverso da Null e il relativo tipo di runtime soddisfa una delle condizioni elencate in precedenza.

Per verificare la presenza di valori non Null, è possibile usare un criterionull costante negato, come illustrato nell'esempio seguente:

if (input is not null)
{
    // ...
}

Per altre informazioni, vedere le sezioni Criterio di dichiarazione e Criterio di tipo delle note sulla proposta di funzionalità.

Criterio costante

Si usa un criterio costante per verificare se il risultato di un'espressione è uguale a una costante specificata, come illustrato nell'esempio seguente:

public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch
{
    1 => 12.0m,
    2 => 20.0m,
    3 => 27.0m,
    4 => 32.0m,
    0 => 0.0m,
    _ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),
};

In un criterio costante, è possibile usare qualsiasi espressione costante, ad esempio:

L'espressione deve essere un tipo convertibile nel tipo costante, con un'eccezione: un'espressione il cui tipo è Span<char> o ReadOnlySpan<char> può essere confrontata con stringhe costanti in C# 11 e versioni successive.

Usare un criterio costante per verificare la presenza di null, come illustrato nell'esempio seguente:

if (input is null)
{
    return;
}

Il compilatore garantisce che non venga richiamato alcun operatore di uguaglianza di overload dall'utente == quando viene valutata l'espressione x is null.

È possibile usare un criterio costante negatonull per verificare la presenza di valori diversi da Null, come illustrato nell'esempio seguente:

if (input is not null)
{
    // ...
}

Per altre informazioni, vedere la sezione criterio costante della nota sulla proposta di funzionalità.

Criteri relazionali

Si usa un criterio relazionale per confrontare il risultato di un'espressione con una costante, come illustrato nell'esempio seguente:

Console.WriteLine(Classify(13));  // output: Too high
Console.WriteLine(Classify(double.NaN));  // output: Unknown
Console.WriteLine(Classify(2.4));  // output: Acceptable

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

In un criterio relazionale è possibile usare uno qualsiasi degli operatori relazionali<, >, <= o >=. La parte destra di un criterio relazionale deve essere un'espressione costante. L'espressione costante può essere di tipo integer, a virgola mobile, char o enumerazione.

Per verificare se un risultato di un'espressione è in un determinato intervallo, trovare la corrispondenza con un criterio and congiuntivo, come illustrato nell'esempio seguente:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 3, 14)));  // output: spring
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 7, 19)));  // output: summer
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 2, 17)));  // output: winter

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    >= 3 and < 6 => "spring",
    >= 6 and < 9 => "summer",
    >= 9 and < 12 => "autumn",
    12 or (>= 1 and < 3) => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
};

Se il risultato di un'espressione è null o non riesce a eseguire la conversione nel tipo di una costante per una conversione nullable o unboxing, un criterio relazionale non corrisponde a un'espressione.

Per altre informazioni, vedere la sezione Criteri relazionali della nota sulla proposta di funzionalità.

Criteri logici

Per creare i notcriteri logiciand seguenti, è possibile usare i combinatori di criteri or, e :

  • Criterio Negazionenot che corrisponde a un'espressione quando il criterio negato non corrisponde all'espressione. Nell'esempio seguente viene illustrato come negare un criterio costantenull per verificare se un'espressione non è Null:

    if (input is not null)
    {
        // ...
    }
    
  • Criterio congiuntivoand che corrisponde a un'espressione quando entrambi i criteri corrispondono all'espressione. L'esempio seguente illustra come combinare criteri relazionali per verificare se un valore si trova in un determinato intervallo:

    Console.WriteLine(Classify(13));  // output: High
    Console.WriteLine(Classify(-100));  // output: Too low
    Console.WriteLine(Classify(5.7));  // output: Acceptable
    
    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",
    };
    
  • Criterio disgiuntivoor che corrisponde a un'espressione quando uno dei due criteri corrisponde all'espressione, come illustrato nell'esempio seguente:

    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));  // output: winter
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));  // output: autumn
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));  // output: spring
    
    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}."),
    };
    

Come illustrato nell'esempio precedente, è possibile usare ripetutamente i combinatori di criteri in un criterio.

Priorità e ordine di controllo

I combinatori di criteri vengono ordinati dalla priorità più alta alla più bassa come indicato di seguito:

  • not
  • and
  • or

Quando un criterio logico è un criterio di un'espressione is, la priorità dei combinatori di criteri logici è superiore alla priorità degli operatori logici (operatori bit per bit logici e booleani logici). In caso contrario, la priorità dei combinatori di criteri logici è inferiore alla priorità degli operatori logici e condizionali. Per l'elenco completo degli operatori C# ordinati per livello di priorità, vedere la sezione Priorità degli operatori nell'articolo Operatori C#.

Per specificare in modo esplicito la priorità, usare le parentesi, come illustrato nell'esempio seguente:

static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');

Nota

L'ordine in cui vengono controllati i criteri non è definito. In fase di esecuzione, è possibile controllare prima i criteri annidati a destra di or e and.

Per altre informazioni, vedere la sezione Combinatori costanti della nota sulla proposta di funzionalità.

Criterio di proprietà

Si usa un criterio di proprietà per trovare una corrispondenza con le proprietà o i campi di un'espressione rispetto ai criteri annidati, come illustrato nell'esempio seguente:

static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };

Un criterio di proprietà corrisponde a un'espressione quando il risultato di un'espressione è diverso da Null e ogni criterio annidato corrisponde alla proprietà o al campo corrispondente del risultato dell'espressione.

È anche possibile aggiungere un controllo del tipo di runtime e una dichiarazione di variabile a un criterio di proprietà, come illustrato nell'esempio seguente:

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

Un criterio di proprietà è un criterio ricorsivo. Ovvero, è possibile usare qualsiasi criterio come criterio annidato. È possibile usare un criterio di proprietà per associare parti di dati a criteri annidati, come illustrato nell'esempio seguente:

public record Point(int X, int Y);
public record Segment(Point Start, Point End);

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start: { Y: 0 } } or { End: { Y: 0 } };

Nell'esempio precedente vengono utilizzati orcombinatori di criteri e tipi di record.

A partire da C# 10, è possibile fare riferimento a proprietà o campi annidati all'interno di un criterio di proprietà. Questa funzionalità è nota come criterio di proprietà esteso. Ad esempio, è possibile effettuare il refactoring del metodo dall'esempio precedente nel codice equivalente seguente:

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start.Y: 0 } or { End.Y: 0 };

Per altre informazioni, vedere la sezione Criterio di proprietà della nota sulla proposta di funzionalità e la nota sulla proposta di funzionalità Criteri di proprietà estesi.

Suggerimento

È possibile usare la regola di stile Semplificare il criterio di proprietà (IDE0170) per migliorare la leggibilità del codice suggerendo posizioni in cui usare criteri di proprietà estesi.

Criterio posizionale

Si usa un criterio posizionale per decostruire il risultato di un'espressione e trovare una corrispondenza con i valori risultanti rispetto ai criteri annidati corrispondenti, come illustrato nell'esempio seguente:

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

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

Nell'esempio precedente, il tipo di un'espressione contiene il criterio Deconstruct, che viene usato per decostruire il risultato di un'espressione. È anche possibile associare espressioni di tipi tupla a criteri posizionali. In questo modo, è possibile associare più input a vari criteri, come illustrato nell'esempio seguente:

static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)
    => (groupSize, visitDate.DayOfWeek) switch
    {
        (<= 0, _) => throw new ArgumentException("Group size must be positive."),
        (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,
        (>= 5 and < 10, DayOfWeek.Monday) => 20.0m,
        (>= 10, DayOfWeek.Monday) => 30.0m,
        (>= 5 and < 10, _) => 12.0m,
        (>= 10, _) => 15.0m,
        _ => 0.0m,
    };

Nell'esempio precedente vengono usati criteri relazionali e logici.

È possibile usare i nomi degli elementi della tupla e il parametro Deconstruct in un criterio posizionale, come illustrato nell'esempio seguente:

var numbers = new List<int> { 1, 2, 3 };
if (SumAndCount(numbers) is (Sum: var sum, Count: > 0))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");  // output: Sum of [1 2 3] is 6
}

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

È anche possibile estendere un criterio posizionale in uno dei modi seguenti:

  • Aggiungere un controllo del tipo di runtime e una dichiarazione di variabile, come illustrato nell'esempio seguente:

    public record Point2D(int X, int Y);
    public record Point3D(int X, int Y, int Z);
    
    static string PrintIfAllCoordinatesArePositive(object point) => point switch
    {
        Point2D (> 0, > 0) p => p.ToString(),
        Point3D (> 0, > 0, > 0) p => p.ToString(),
        _ => string.Empty,
    };
    

    Nell'esempio precedente vengono utilizzati record posizionali che forniscono in modo implicito il metodo Deconstruct.

  • È possibile usare un criterio di proprietà all'interno di un criterio posizionale, come illustrato nell'esempio seguente:

    public record WeightedPoint(int X, int Y)
    {
        public double Weight { get; set; }
    }
    
    static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };
    
  • Combinare due utilizzi precedenti, come illustrato nell'esempio seguente:

    if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p)
    {
        // ..
    }
    

Un criterio posizionale è un criterio ricorsivo. Ovvero, è possibile usare qualsiasi criterio come criterio annidato.

Per altre informazioni, vedere la sezione Criterio posizionale della nota sulla proposta di funzionalità.

Criterio var

È possibile usare un criterio var per trovare una corrispondenza con qualsiasi espressione, inclusa null, e assegnarne il risultato a una nuova variabile locale, come illustrato nell'esempio seguente:

static bool IsAcceptable(int id, int absLimit) =>
    SimulateDataFetch(id) is var results 
    && results.Min() >= -absLimit 
    && results.Max() <= absLimit;

static int[] SimulateDataFetch(int id)
{
    var rand = new Random();
    return Enumerable
               .Range(start: 0, count: 5)
               .Select(s => rand.Next(minValue: -10, maxValue: 11))
               .ToArray();
}

Un criterio var è utile quando è necessaria una variabile temporanea all'interno di un'espressione booleana per contenere il risultato dei calcoli intermedi. È anche possibile usare un criterio var quando è necessario eseguire più controlli in caso di guard when di un'espressione o di un'istruzione switch, come illustrato nell'esempio seguente:

public record Point(int X, int Y);

static Point Transform(Point point) => point switch
{
    var (x, y) when x < y => new Point(-x, y),
    var (x, y) when x > y => new Point(x, -y),
    var (x, y) => new Point(x, y),
};

static void TestTransform()
{
    Console.WriteLine(Transform(new Point(1, 2)));  // output: Point { X = -1, Y = 2 }
    Console.WriteLine(Transform(new Point(5, 2)));  // output: Point { X = 5, Y = -2 }
}

Nell'esempio precedente, il criterio var (x, y) equivale a un criterio posizionale(var x, var y).

In un criterio var, il tipo di una variabile dichiarata è il tipo in fase di compilazione dell'espressione corrispondente al criterio.

Per altre informazioni, vedere la sezione criterio Var della nota sulla proposta di funzionalità.

Criterio discard

Per trovare una corrispondenza con qualsiasi espressione, inclusa null, si usa un criterio discard_, come illustrato nell'esempio seguente:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));  // output: 5.0
Console.WriteLine(GetDiscountInPercent(null));  // output: 0.0
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));  // output: 0.0

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

Nell'esempio precedente viene usato un criterio discard per gestire null e qualsiasi valore intero che non ha il membro corrispondente dell'enumerazione DayOfWeek. Ciò garantisce che un'espressione switch nell'esempio gestisca tutti i possibili valori di input. Se non si usa un criterio di discard in un'espressione switch e nessuno dei criteri dell'espressione corrisponde a un input, il runtime genera un'eccezione. Il compilatore genera un avviso se un'espressione switch non gestisce tutti i possibili valori di input.

Un criterio di discard non può essere un criterio in un'espressione is o un'istruzione switch. In questi casi, per trovare una corrispondenza con qualsiasi espressione, usare un var criterio con un oggetto discard: var _. Un criterio discard può essere il criterio in un'espressione switch.

Per altre informazioni, vedere la sezione Criterio discard della nota sulla proposta di funzionalità.

Criterio tra parentesi

È possibile inserire parentesi intorno a qualsiasi criterio. In genere, è necessario sottolineare o modificare la priorità nei criteri logici, come illustrato nell'esempio seguente:

if (input is not (float or double))
{
    return;
}

Criteri elenco

A partire da C# 11, è possibile associare una matrice o un elenco a una sequenza di criteri, come illustrato nell'esempio seguente:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]);  // True

Come illustrato nell'esempio precedente, un criterio di elenco viene confrontato quando ogni criterio annidato corrisponde all'elemento corrispondente di una sequenza di input. È possibile usare qualsiasi criterio all'interno di un criterio di elenco. Per trovare una corrispondenza con qualsiasi elemento, usare il criterio discard o, se si vuole acquisire anche l'elemento, il criterio var, come illustrato nell'esempio seguente:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

Gli esempi precedenti corrispondono a un'intera sequenza di input rispetto a un criterio di elenco. Per trovare la corrispondenza degli elementi solo all'inizio o/e alla fine di una sequenza di input, usare il criterio di sezione.., come illustrato nell'esempio seguente:

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

Un criterio di sezione corrisponde a zero o più elementi. È possibile usare al massimo un criterio di sezione in un criterio di elenco. Il criterio di sezione può essere visualizzato solo in un criterio di elenco.

È anche possibile annidare un criterio secondario all'interno di un criterio di sezione, come illustrato nell'esempio seguente:

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron");  // output: Message apron doesn't match.

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });  // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

Per altre informazioni, vedere la nota relativa alla proposta di funzionalità Criteri di elenco.

Specifiche del linguaggio C#

Per altre informazioni, vedere la sezione Criteri and criteri di ricerca della Specifica del linguaggio C#.

Per informazioni sulle funzionalità aggiunte in C# 8 e versioni successive, vedere le note sulla proposta delle funzionalità seguenti:

Vedi anche