Ce face switch expression și Pattern Matching
Expresia switch (introdusă în C# 8 și extinsă masiv în C# 9–12) transformă instrucțiunea clasică switch / case / break într-o expresie funcțională compactă care returnează direct o valoare. Compilatorul verifică exhaustivitatea ramurilor: dacă nu acoperi toate cazurile posibile și omiți ramura implicită discard (_ => ...), primești un avertisment la compilare.
Combinată cu Pattern Matching (și cu operatorul is în condiții if), expresia switch poate testa simultan tipul unui obiect (type pattern), intervale numerice cu >, <=, and, or, not (relational & logical patterns), proprietăți interne ale obiectelor (property pattern: { Status: "Activ", Sold: > 1000m }), tupluri și chiar structura tablourilor sau a listelor (list patterns în C# 11: [var primul, .., var ultimul]).
Parametri, metode și opțiuni
| Parametru / Metodă | Descriere |
|---|---|
val switch { ... } |
Evaluează val față de fiecare șablon în ordine și returnează expresia din dreapta săgeții =>. |
> 0 and <= 100 |
Șabloane relaționale și logice (and, or, not) pentru testarea intervalelor fără a repeta variabila. |
Tip { Prop: val } t |
Șablon de tip și proprietăți: verifică dacă obiectul este de tipul Tip, îi testează proprietățile și îl leagă la variabila t. |
when conditie |
Gardă suplimentară (case guard) evaluată doar dacă șablonul principal s-a potrivit. |
[primul, .. mijloc, ultimul] |
Șablon de listă (C# 11+): potrivește elemente din T[], List<T> sau Span<T> și extrage sub-secvențe cu ... |
_ => ... |
Șablonul discard (implicit / default): prinde orice valoare care nu s-a potrivit pe ramurile anterioare. |
Exemple
Exemplul 1: Calculul costului de livrare cu property, relational și tuple patterns
Verificăm simultan proprietățile unei comenzi (țară, valoare totală, abonament VIP) într-o singură expresie declarativă, fără lanțuri complexe de if / else if.
decimal CalculeazaTransport(Comanda c) => c switch
{
{ Anulata: true } => 0m,
{ MembruVip: true } => 0m,
{ Tara: "RO", Total: >= 200m } => 0m,
{ Tara: "RO", Total: < 200m } => 19.99m,
{ Tara: "MD" or "BG" or "HU" } => 45.00m,
_ => 89.00m
};
var comenzi = new[]
{
new Comanda("RO", 250m, MembruVip: false, Anulata: false),
new Comanda("RO", 85m, MembruVip: false, Anulata: false),
new Comanda("HU", 150m, MembruVip: false, Anulata: false),
new Comanda("DE", 60m, MembruVip: true, Anulata: false)
};
foreach (var cmd in comenzi)
{
Console.WriteLine($"{cmd.Tara} ({cmd.Total} RON, VIP={cmd.MembruVip}) -> Transport: {CalculeazaTransport(cmd)} RON");
}
record Comanda(string Tara, decimal Total, bool MembruVip, bool Anulata);
Ieșire în consolă
RO (250 RON, VIP=False) -> Transport: 0 RON
RO (85 RON, VIP=False) -> Transport: 19.99 RON
HU (150 RON, VIP=False) -> Transport: 45.00 RON
DE (60 RON, VIP=True) -> Transport: 0 RON
Exemplul 2: Pattern matching cu operatorul is not null și list patterns (C# 11+)
Putem analiza forma unui vector de argumente sau a unui rând CSV folosind șabloane de listă [..] și putem verifica absența lui null cu is not null.
string AnalizeazaComanda(string[] tokeni) => tokeni switch
{
[] => "Comandă goală.",
["help"] or ["--help"] or ["-h"] => "Afișare meniu de ajutor.",
["deploy", var mediu] => $"Deploy în mediul '{mediu}' cu opțiuni implicite.",
["deploy", var mediu, .. var optiuni] => $"Deploy în '{mediu}' cu {optiuni.Length} opțiuni: {string.Join(", ", optiuni)}",
_ => $"Comandă necunoscută: {tokeni[0]}"
};
Console.WriteLine(AnalizeazaComanda(new[] { "--help" }));
Console.WriteLine(AnalizeazaComanda(new[] { "deploy", "staging" }));
Console.WriteLine(AnalizeazaComanda(new[] { "deploy", "prod", "--force", "--no-backup" }));
Ieșire în consolă
Afișare meniu de ajutor.
Deploy în mediul 'staging' cu opțiuni implicite.
Deploy în 'prod' cu 2 opțiuni: --force, --no-backup
Greșeli frecvente
- Plasarea unui șablon general înaintea unuia mai specific: dacă pui
{ Tara: "RO" } => 19.99mînainte de{ Tara: "RO", Total: >= 200m } => 0m, compilatorul raportează eroareaCS8510: The pattern has already been handled by a previous arm of the switch expression. - Folosirea operatorului
!= nullîn loc deis not nullatunci când clasa supraîncarcă operatorul==— șablonulis not nullgarantează verificarea strictă a referinței fără a apela operatori customizați.
Funcții și concepte înrudite
Tipuri Nullable (?, ??, ?.): Reprezintă explicit absența unei valori și previne excepțiile NullReferenceException prin operatori siguri de navigare și coalescență.record și imutabilitate: Definește într-o singură linie modele de date imutabile cu egalitate bazată pe valoare, afișare automată și clonare nedistructivă (with).var și tipurile de date (int, decimal, bool): Declară variabile puternic tipizate prin inferență la compilare (var) sau folosind tipurile fundamentale de valoare din .NET.