Pattern matching en C# : aller droit au but

Introduit en C# 7.0, le pattern matching a été enrichi dans les versions suivantes (patterns de propriété, positionnels, de liste et switch expressions). Le pattern matching modernise tes conditions en C#. Tu décris la forme des données plutôt que de chaîner des if. Voici les types de patterns utiles au quotidien, chacun avec un exemple concret.

Types de patterns

Cas d’usage

Type pattern

Affiner le type à la volée pour accéder à des membres spécifiques.

// Handle animals differently by type
void Greet(Animal a)
{
    if (a is Dog d)
    {
        // Access Dog-specific members
        Console.WriteLine("Woof from " + d.Name);
    }
    else if (a is Cat c)
    {
        Console.WriteLine("Meow from " + c.Name);
    }
}
Constant pattern

Comparer une valeur avec une constante (y compris null).

// Compare with constants
string? state = GetState();
if (state is null)
{
    // Handle missing state
    return;
}
if (state is "Ready")
{
    // Ready branch
}
Relational pattern

Comparer via <, <=, >, >= directement dans le pattern.

// Classify by range
string Classify(int score) => score switch
{
    < 0 => "invalid",
    < 50 => "low",
    < 80 => "medium",
    <= 100 => "high",
    _ => "invalid"
};
Logical patterns

Combiner des patterns avec and, or, not.

// Combine relational patterns
bool IsTeenager(int age) => age is >= 13 and <= 19;

// Guard invalid values
bool IsValid(int x) => x is not < 0;
Property pattern

Faire matcher un objet sur des propriétés et valeurs.

// Match on properties
bool IsAdmin(User u) => u is { Role: "Admin", Active: true };

// Nested properties
bool IsWorkingInParis(Employee e) => e is { Address.City: "Paris" };
Positional pattern

Déconstruire et vérifier une forme via Deconstruct().

// Match by position (uses record positional members)
public readonly record struct Point(int X, int Y);

string Quadrant(Point p) => p switch
{
    Point(0, 0) => "origin",
    Point(> 0, > 0) => "Q1",
    Point(< 0, > 0) => "Q2",
    Point(< 0, < 0) => "Q3",
    Point(> 0, < 0) => "Q4",
    _ => "axis"
};
List patterns

Faire matcher des séquences (début, fin, éléments).

// Match list shapes
string Describe(int[] data) => data switch
{
    [] => "empty",
    [var single] => $"single {single}",
    [0, .. var rest] => $"starts with zero, count={rest.Length}",
    [.., 42] => "ends with 42",
    _ => "other"
};
var / discard

Capturer une valeur ou l’ignorer dans un pattern.

// Capture or discard
if (GetValue() is var value and > 0)
{
    // Use captured value
    Console.WriteLine(value);
}

if (GetValue() is _)
{
    // Matches anything (discard)
}
Switch expression

Exprimer une logique par forme, court et lisible.

// Price by product shape
decimal PriceOf(Product p) => p switch
{
    Book { Pages: > 500 } => 29.90m,
    Book => 19.90m,
    Subscription { Tier: "Pro" } => 49m,
    Subscription { Tier: "Basic" } => 9m,
    _ => 0m
};

Résumé

Utilise les patterns pour encoder des règles métier simples sans if imbriqués. Idéal pour classifier, router ou valider des données.

Avantages
  • Plus lisible qu’une cascade de if/else.
  • Moins d’erreurs: des formes explicites et exhaustivité avec _.
  • Combine bien avec switch expression.
Inconvénients
  • Peut devenir dense si la logique est trop complexe.
  • Nécessite .NET/C# récents pour tous les patterns.

Bonnes pratiques

  • Préférer switch expression pour la lisibilité quand c’est possible.
  • Rester explicite: nomme les propriétés utilisées dans les patterns.
  • Ajouter un cas _ par défaut et tracer l’exception si inattendu.
Écrit le 2025-08-29