1. Wprowadzenie
Już umiesz pisać taki kod:
switch (color)
{
case "red":
Console.WriteLine("Stop!");
break;
case "yellow":
Console.WriteLine("Czekaj...");
break;
case "green":
Console.WriteLine("Możesz iść!");
break;
default:
Console.WriteLine("A co to za kolor?");
break;
}
Ale co jeśli chcemy przypisać zmiennej różną wartość w zależności od parametru wejściowego, na przykład zwrócić string oparty na liczbie z funkcji? Przed pojawieniem się switch-wyrażeń trzeba było pisać takie konstrukcje:
string znaczenie;
switch (number)
{
case 42:
znaczenie = "Odpowiedź na główne pytanie życia, wszechświata i w ogóle wszystkiego";
break;
case 7:
znaczenie = "Szczęśliwa liczba";
break;
default:
znaczenie = "Zwykła liczba";
break;
}
Trochę to mało eleganckie jak na język XXI wieku, nie? Chciałoby się od razu zwrócić wartość, jak to bywa w językach funkcyjnych.
I tu właśnie pojawiają się switch-wyrażenia — jedna z najfajniejszych (i najświeższych) możliwości C#, od wersji 8.0.
2. switch-wyrażenia: składnia, którą chce się przytulić
Porównajmy: operator i wyrażenie
Klasyczny switch to statement ("operator"), a switch-wyrażenie to expression, czyli oblicza wartość i można ją od razu przypisać do zmiennej albo zwrócić z metody.
Struktura switch-wyrażenia:
var result = value switch
{
pattern1 => expression1,
pattern2 => expression2,
_ => defaultExpression // "podkreślenie" - to "inaczej"
};
Na przykład:
int number = 42;
string znaczenie = number switch
{
42 => "Odpowiedź na główne pytanie życia, wszechświata i w ogóle wszystkiego",
7 => "Szczęśliwa liczba",
_ => "Zwykła liczba"
};
Console.WriteLine(znaczenie); // => Odpowiedź na główne pytanie życia, wszechświata i w ogóle wszystkiego
Wszystko zwięźle, czytelnie, żadnych tasiemców kodu, ani jednego break;.
Zwróć uwagę: podkreślenie _ — to "złap wszystkie pozostałe przypadki", odpowiednik default.
3. switch-wyrażenia i wzorce: jak to się łączy
I tu zaczyna się prawdziwa zabawa. W switch-wyrażeniach możesz używać nie tylko zwykłych wartości, ale też wzorców: sprawdzania zakresu, typu, właściwości obiektów, dekompozycji (rozbijania obiektu na części), a nawet combinatorial patterns (and, or, not).
Przykłady wzorców w switch-wyrażeniu
Wzorce stałe
To właśnie to, co robiliśmy wyżej: porównanie z 42, 7 itd.
Zakresy i porównania
int wiek = 17;
string kategoria = wiek switch
{
< 18 => "Niepełnoletni",
>= 18 and <= 65 => "Dorosły",
> 65 => "Osoba starsza",
_ => "Nieznana kategoria wiekowa"
};
Console.WriteLine(kategoria);
Przy okazji, zwróć uwagę — można używać kilku warunków naraz! C# robi się coraz bardziej wyrazisty.
Wzorce typów (Type Patterns)
Załóżmy, że mamy obiekt, który może być różnego typu (np. object value;). Możemy obsłużyć różne typy tak:
object value = 3.14;
string opis = value switch
{
int i => $"Liczba całkowita: {i}",
double d => $"Liczba zmiennoprzecinkowa: {d:F2}",
string s => $"To jest string: '{s}'",
null => "null!",
_ => "Coś innego"
};
Console.WriteLine(opis); // => Liczba zmiennoprzecinkowa: 3.14
Wzorce powiązane (Property Patterns i Positional Patterns)
Od C# 8 i dalej możemy sprawdzać wartości właściwości bezpośrednio w switch-wyrażeniu.
Przykład z obiektem
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
User user = new User { Name = "Anna", Age = 17 };
string powitanie = user switch
{
{ Age: < 18 } => $"Cześć, {user.Name}! Jesteś nastolatkiem.",
{ Age: >= 18 and <= 65 } => $"Hej, {user.Name}! Dorosłe życie jest super.",
{ Age: > 65 } => $"Witamy, {user.Name}! Zawsze miło Cię widzieć.",
_ => "Nieznany wiek"
};
Console.WriteLine(powitanie);
Tutaj w klamrach { Age: < 18 } — to property pattern, czyli sprawdzamy nie cały obiekt, a tylko jego właściwość.
4. Nowe wzorce C# 11-14
C# dąży do zwięzłości i elastyczności. Ostatnio pojawiły się w nim combinatorial patterns: and, or, not, a także ulepszone positional patterns. Zobaczmy je w akcji.
Wzorce logiczne: and, or, not
Możesz budować złożone warunki jak w matematycznej logice!
int temperatura = 25;
string wynik = temperatura switch
{
< 0 => "Mróz!",
>= 0 and < 20 => "Chłodno",
>= 20 and < 30 => "Komfortowo",
>= 30 or <= -30 => "Ekstremalnie!",
_ => "Dziwna temperatura"
};
Jeśli chcesz wykluczyć przypadki, proszę bardzo:
int wynikTestu = 80;
string ocena = wynikTestu switch
{
>= 90 => "Świetnie",
>= 60 and not >= 90 => "Dobrze",
_ => "Słabo"
};
Dekonstrukcja (Positional Patterns)
Jeśli masz strukturę lub record, który wspiera dekonstrukcję, C# pozwala rozbić obiekt bezpośrednio w switch-wyrażeniu:
public record Point(int X, int Y);
Point p = new Point(10, 20);
string opis = p switch
{
(0, 0) => "Początek układu współrzędnych",
(var x, 0) => $"Na osi X, X={x}",
(0, var y) => $"Na osi Y, Y={y}",
(var x, var y) when x == y => $"Na przekątnej: {x}",
_ => $"W punkcie ({p.X}, {p.Y})"
};
Console.WriteLine(opis); // => W punkcie (10, 20)
5. Kiedy i po co używać switch-wyrażeń
W nowoczesnym C# switch-wyrażenia to nie tylko modny "ficzer", ale realny sposób na uniknięcie klepania kodu, zrobienie go bardziej kompaktowym, łatwiejszym do testowania i utrzymania. To szczególnie ważne przy pisaniu parserów, handlerów komend, logiki biznesowej, konfiguracji reguł — wszędzie tam, gdzie trzeba rozgałęziać wykonanie po złożonych kryteriach, nie tylko po wartościach, ale też po typach, zakresach, właściwościach, kombinacjach.
Na rozmowie rekrutacyjnej
- Jak obsłużyłbyś różne warianty odpowiedzi od użytkownika?
- Jak zrobić, żeby zwrócić różną wartość funkcji zależną i od typu, i od wartości?
- Jak łatwo dodawać nowe reguły bez tony if-else?
Pokaż switch-wyrażenie z wzorcami! To od razu pokaże, że znasz nowe możliwości języka i masz dobry styl kodowania.
Tabela: porównanie switch-operatora i switch-wyrażenia
| Charakterystyka | switch statement | switch expression |
|---|---|---|
| Typ | operator (statement) | wyrażenie (expression) |
| Użycie break | obowiązkowe | niepotrzebny |
| Może zwracać wartość | tylko przez zmienną | tak, od razu zwraca wartość |
| Może używać wzorców | częściowo | w pełni (type, property, itd.) |
| Pułapka | można zapomnieć break | wszystkie ścieżki muszą zwracać wartość |
6. Typowe błędy i drobne złośliwości
- Początkujący programiści często mylą różnicę między switch-operatorem ("statement") a switch-wyrażeniem ("expression"). Na przykład zapominają, że w switch-wyrażeniu nie potrzeba break;, a wszystkie gałęzie muszą koniecznie zwracać wartość.
- Jeśli zapomnisz gałęzi _, kompilator podpowie: "a co jeśli nic nie pasuje?".
- W switch-wyrażeniu nie można używać goto (i całe szczęście!).
- Jeśli powtórzysz warunek (np. dwa takie same wzorce), kompilator też tego nie przepuści.
- Zwracaj uwagę na typ wartości zwracanej z switch-wyrażenia: musi być zgodny z typem zmiennej, do której to wszystko przypisujesz.
GO TO FULL VERSION