Partager via


Correspondance des modèles : les is expressions et switch les opérateurs and, oret not dans les modèles

Vous utilisez l’expression, l’instruction isswitch et l’expression switch pour faire correspondre une expression d’entrée à un nombre quelconque de caractéristiques. C# prend en charge plusieurs modèles, notamment la déclaration, le type, la constante, la relation, la propriété, la liste, var et discard. Les modèles peuvent être combinés à l’aide des mots clés logiques booléens and, or et not.

Les expressions et instructions C# suivantes prennent en charge les critères spéciaux :

Dans ces constructions, vous pouvez faire correspondre une expression d’entrée à l’un des modèles suivants :

  • Modèle de déclaration : pour vérifier le type d’exécution d’une expression et, si une correspondance réussit, affecter un résultat d’expression à une variable déclarée.
  • Modèle de type : pour vérifier le type d’exécution d’une expression.
  • Modèle constant : pour tester qu’un résultat d’expression est égal à une constante spécifiée.
  • Modèles relationnels : pour comparer un résultat d’expression avec une constante spécifiée.
  • Modèles logiques : pour tester qu’une expression correspond à une combinaison logique de modèles.
  • Modèle de propriété : pour tester que les propriétés ou les champs d’une expression correspondent aux modèles imbriqués.
  • Modèle positionnel : pour déconstruire un résultat d’expression et tester si les valeurs résultantes correspondent à des modèles imbriqués.
  • Modèle var : pour faire correspondre n’importe quelle expression et affecter son résultat à une variable déclarée.
  • Modèle Discard : pour faire correspondre n’importe quelle expression.
  • Modèles de liste : pour tester qu’une séquence d’éléments correspond aux modèles imbriqués correspondants. Introduit dans C# 11.

Les modèles logiques, de propriété, positionnels et de liste sont des modèles récursifs. Autrement dit, ils peuvent contenir des modèles imbriqués.

Pour voir un exemple d’utilisation de ces modèles afin de créer un algorithme piloté par les données, consultez le Tutoriel : Utiliser des critères spéciaux pour générer des algorithmes pilotés par type et pilotés par des données.

Modèles de déclaration et de type

Utilisez des modèles de déclaration et de type pour vérifier si le type d’exécution d’une expression est compatible avec un type donné. Avec un modèle de déclaration, vous pouvez également déclarer une nouvelle variable locale. Lorsqu’un modèle de déclaration correspond à une expression, cette variable reçoit un résultat d’expression convertie, comme l’illustre l’exemple suivant :

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

Un modèle de déclaration avec le type T correspond à une expression lorsqu’un résultat d’expression n’est pas null et que l’une des conditions suivantes est vraie :

  • Le type d’exécution d’un résultat d’expression est T.

  • Le type d’exécution d’un résultat d’expression dérive du type T, implémente l’interface T, ou une autre conversion de référence implicite en T existe à partir de celui-ci. L’exemple suivant illustre deux cas où cette condition a la valeur 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,
    };
    

    Dans l’exemple précédent, au premier appel à la méthode GetSourceLabel, le premier modèle correspond à une valeur d’argument, car le type d’exécution int[] de l’argument dérive du type Array. Au deuxième appel à la méthode GetSourceLabel, le type d’exécution List<T> de l’argument ne dérive pas du type Array, mais implémente l’interface ICollection<T>.

  • Le type d’exécution d’un résultat d’expression est un type de valeur pouvant accepter la valeur Null avec le type sous-jacent T.

  • Il existe une conversion boxing ou unboxing en type Tà partir du type d’exécution d’un résultat d’expression.

L’exemple suivant illustre les deux dernières conditions :

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

Si vous souhaitez vérifier uniquement le type d’une expression, vous pouvez utiliser un caractère _ de type Discard à la place du nom d’une variable, comme l’illustre l’exemple suivant :

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

À cet effet, vous pouvez utiliser un modèle de type, comme l’illustre l’exemple suivant :

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

À l’instar d’un modèle de déclaration, un modèle de type correspond à une expression lorsqu’un résultat d’expression est non null et que son type d’exécution satisfait à l’une des conditions précédentes.

Pour rechercher des valeurs non-null, vous pouvez utiliser un modèle de constante null inversé, comme le montre l’exemple suivant :

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

Pour plus d’informations, consultez les sections Modèle de déclaration et Modèle de type des notes de proposition de fonctionnalités.

Modèle de constante

Vous utilisez un modèle de constante pour tester si un résultat d’expression est égal à une constante spécifiée, comme l’illustre l’exemple suivant :

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

Dans un modèle constant, vous pouvez utiliser n’importe quelle expression constante, par exemple :

L’expression doit être un type convertible en type constant, à une exception près : une expression dont le type est Span<char> ou ReadOnlySpan<char> peut être mise en correspondance avec des chaînes constantes en C# 11 et versions ultérieures.

Utilisez un modèle constant pour rechercher null, comme l’illustre l’exemple suivant :

if (input is null)
{
    return;
}

Le compilateur garantit qu’aucun opérateur d’égalité == surchargé par l’utilisateur n’est appelé lorsque l’expression x is null est évaluée.

Vous pouvez utiliser un modèle de constante inversé null pour rechercher des valeurs non-null, comme le montre l’exemple suivant :

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

Pour plus d’informations, consultez la section Modèle de constante de la note de proposition de fonctionnalités.

Modèles relationnels

Vous utilisez un modèle relationnel pour comparer un résultat d’expression à une constante, comme l’illustre l’exemple suivant :

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

Dans un modèle relationnel, vous pouvez utiliser l’un des opérateurs relationnels <, >, <= ou >=. La partie droite d’un modèle relationnel doit être une expression constante. L’expression constante peut être d’un type entier, à virgule flottante, char ou énumération.

Pour vérifier si un résultat d’expression se trouve dans une certaine plage, faites-le correspondre à un modèle and conjonctif, comme l’illustre l’exemple suivant :

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

Si un résultat d’expression est null ou ne parvient pas à se convertir en type de constante par une conversion nullable ou unboxing, un modèle relationnel ne correspond pas à une expression.

Pour plus d’informations, consultez la section Modèles relationnels de la note de proposition de fonctionnalités.

Modèles logiques

Vous utilisez les combinateurs de modèles not, and et or pour créer les modèles logiques suivants :

  • Modèle de négation not qui correspond à une expression lorsque le modèle inversé ne correspond pas à l’expression. L’exemple suivant montre comment inverser un modèle de constante null pour vérifier si une expression est non-null :

    if (input is not null)
    {
        // ...
    }
    
  • Modèle conjonctif and qui correspond à une expression lorsque les deux modèles correspondent à l’expression. L’exemple suivant montre comment combiner des modèles relationnels pour vérifier si une valeur se trouve dans une certaine plage :

    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",
    };
    
  • Modèle disjonctif or qui correspond à une expression lorsque l’un ou l’autre modèle correspond à l’expression, comme le montre l’exemple suivant :

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

Comme l’illustre l’exemple précédent, vous pouvez utiliser à plusieurs reprises les combinateurs de modèles dans un modèle.

Priorité et ordre de vérification

Les combinateurs de modèle sont classés en fonction de l’ordre de liaison des expressions comme suit :

  • not
  • and
  • or

Le not modèle se lie d’abord à son opérande. Le and modèle se lie après toute not liaison d’expression de modèle. Le or modèle se lie après tous not et and les modèles sont liés à des opérandes. L’exemple suivant tente de faire correspondre tous les caractères qui ne sont pas des lettres minuscules. a - z Il a une erreur, car le not modèle se lie avant le and modèle :

// Incorrect pattern. `not` binds before `and`
static bool IsNotLowerCaseLetter(char c) => c is not >= 'a' and <= 'z';

La liaison par défaut signifie que l’exemple précédent est analysé comme l’exemple suivant :

// The default binding without parentheses is shows in this method. `not` binds before `and`
static bool IsNotLowerCaseLetterDefaultBinding(char c) => c is ((not >= 'a') and <= 'z');

Pour résoudre ce problème, vous devez spécifier que vous souhaitez que la not liaison soit liée à l’expression >= 'a' and <= 'z' :

// Correct pattern. Force `and` before `not`
static bool IsNotLowerCaseLetterParentheses(char c) => c is not (>= 'a' and <= 'z');

L’ajout de parenthèses devient plus important, car vos modèles deviennent plus compliqués. En général, utilisez des parenthèses pour clarifier vos modèles pour d’autres développeurs, comme l’illustre l’exemple suivant :

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

Remarque

L’ordre dans lequel les modèles ayant le même ordre de liaison sont vérifiés n’est pas défini. Au moment de l’exécution, les modèles imbriqués à droite de plusieurs or modèles et plusieurs and modèles peuvent être vérifiés en premier.

Pour plus d’informations, consultez la section Combinateurs de modèles de la note de proposition de fonctionnalités.

Modèle de propriété

Vous utilisez un modèle de propriété pour faire correspondre les propriétés ou les champs d’une expression aux modèles imbriqués, comme l’illustre l’exemple suivant :

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

Un modèle de propriété correspond à une expression lorsqu’un résultat d’expression est non-null et chaque modèle imbriqué correspond à la propriété ou au champ correspondant du résultat de l’expression.

Vous pouvez également ajouter une vérification de type d’exécution et une déclaration de variable à un modèle de propriété, comme l’illustre l’exemple suivant :

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 modèle de propriété est un modèle récursif. Autrement dit, vous pouvez utiliser n’importe quel modèle comme modèle imbriqué. Utilisez un modèle de propriété pour faire correspondre des parties de données à des modèles imbriqués, comme l’illustre l’exemple suivant :

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

L’exemple précédent utilise le combinateur de modèles or et les types d’enregistrements.

À compter de C# 10, vous pouvez référencer des propriétés ou des champs imbriqués dans un modèle de propriété. Cette fonctionnalité est appelée modèle de propriété étendu. Par exemple, vous pouvez refactoriser la méthode de l’exemple précédent dans le code équivalent suivant :

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

Pour plus d’informations, consultez la section Modèle de propriété de la note de proposition de fonctionnalités et la note de proposition de fonctionnalités Modèles de propriétés étendus.

Conseil

Vous pouvez utiliser la règle de style Simplifier le modèle de propriété (IDE0170) pour améliorer la lisibilité du code en suggérant des emplacements pour utiliser des modèles de propriétés étendus.

Modèle positionnel

Vous utilisez un modèle positionnel pour déconstruire un résultat d’expression et faire correspondre les valeurs obtenues avec les modèles imbriqués correspondants, comme l’illustre l’exemple suivant :

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

Dans l’exemple précédent, le type d’une expression contient la méthode Deconstruct, qui est utilisée pour déconstruire un résultat d’expression.

Important

L'ordre des membres d'un motif positionnel doit correspondre à l'ordre des paramètres de la méthode Deconstruct. C'est parce que le code généré pour le motif positionnel appelle la méthode Deconstruct.

Vous pouvez également faire correspondre des expressions de types de tuple avec des modèles positionnels. De cette façon, vous pouvez faire correspondre plusieurs entrées avec différents modèles, comme l’illustre l’exemple suivant :

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

L’exemple précédent utilise des modèles relationnels et logiques.

Vous pouvez utiliser les noms des éléments de tuple et les paramètres Deconstruct dans un modèle positionnel, comme l’illustre l’exemple suivant :

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

Vous pouvez également étendre un modèle positionnel de l’une des manières suivantes :

  • Ajoutez une vérification de type d’exécution et une déclaration de variable, comme l’illustre l’exemple suivant :

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

    L’exemple précédent utilise des enregistrements positionnels qui fournissent implicitement la méthode Deconstruct.

  • Utilisez un modèle de propriété dans un modèle positionnel, comme l’illustre l’exemple suivant :

    public record WeightedPoint(int X, int Y)
    {
        public double Weight { get; set; }
    }
    
    static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };
    
  • Combinez deux utilisations précédentes, comme l’illustre l’exemple suivant :

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

Un modèle positionnel est un modèle récursif. Autrement dit, vous pouvez utiliser n’importe quel modèle comme modèle imbriqué.

Pour plus d’informations, consultez la section Modèle positionnel de la note de proposition de fonctionnalités.

var motif

Vous utilisez un modèle var pour faire correspondre n’importe quelle expression, y compris null, et affecter son résultat à une nouvelle variable locale, comme l’illustre l’exemple suivant :

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 modèle var est utile lorsque vous avez besoin qu’une variable temporaire dans une expression booléenne contienne le résultat des calculs intermédiaires. Vous pouvez également utiliser un modèle var lorsque vous devez effectuer davantage de vérifications dans les gardes de cas when d’une expression ou d’une instruction switch, comme l’illustre l’exemple suivant :

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

Dans l’exemple précédent, le modèle var (x, y) équivaut à un modèle positionnel (var x, var y).

Dans un modèle var, le type d’une variable déclarée est le type au moment de la compilation de l’expression qui est mis en correspondance avec le modèle.

Pour plus d’informations, consultez la section Modèle var de la note de proposition de fonctionnalités.

Ignorer le modèle

Vous utilisez un modèle discard _ pour faire correspondre n’importe quelle expression, y compris null, comme le montre l’exemple suivant :

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

Dans l’exemple précédent, 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 qu’une expression switch dans l’exemple gère toutes les valeurs d’entrée possibles. Si vous n’utilisez pas de modèle d’abandon dans une expression switch et qu’aucun des modèles de l’expression ne correspond à une entrée, le runtime lève une exception. Le compilateur génère un avertissement si une expression switch ne gère pas toutes les valeurs d’entrée possibles.

Un modèle d’abandon ne peut pas être un modèle dans une expression is ou une instruction switch. Dans ces cas, pour faire correspondre n’importe quelle expression, utilisez un modèle var avec un caractère d’abandon : var _. Un modèle d’abandon peut être un modèle dans une expression switch.

Pour plus d’informations, consultez la section Modèle d’abandon de la note de proposition de fonctionnalités.

Modèle entre parenthèses

Vous pouvez placer des parenthèses autour de n’importe quel modèle. En règle générale, vous devez mettre en évidence ou modifier la priorité dans des modèles logiques, comme l’illustre l’exemple suivant :

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

Modèles de liste

À compter de C# 11, vous pouvez faire correspondre un tableau ou une liste à une séquence de modèles, comme l’illustre l’exemple suivant :

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

Comme l’illustre l’exemple précédent, un modèle de liste est mis en correspondance lorsque chaque modèle imbriqué correspond à l’élément correspondant d’une séquence d’entrée. Vous pouvez utiliser n’importe quel modèle dans un modèle de liste. Pour faire correspondre n’importe quel élément, utilisez le modèle d’abandon ou, si vous souhaitez également capturer l’élément, le modèle var, comme l’illustre l’exemple suivant :

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.

Les exemples précédents correspondent à une séquence d’entrée entière par rapport à un modèle de liste. Pour faire correspondre des éléments uniquement au début ou/et à la fin d’une séquence d’entrée, utilisez le modèle de section .., comme le montre l’exemple suivant :

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 modèle de section correspond à zéro ou plusieurs éléments. Vous pouvez utiliser au plus un modèle de section dans un modèle de liste. Le modèle de section ne peut apparaître que dans un modèle de liste.

Vous pouvez également imbriquer un sous-modèle dans un modèle de section, comme l’illustre l’exemple suivant :

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

Pour plus d’informations, consultez la note de proposition de fonctionnalités Modèles de liste.

spécification du langage C#

Pour plus d’informations, consultez la section Modèles et correspondances de modèles de la spécification du langage C#.

Pour plus d’informations sur les fonctionnalités ajoutées en C# 8 et ultérieur, consultez la note de propositions de fonctionnalités :

Voir aussi