• Programowanie
  • Switch expression w C# - wzorce i pułapki dopasowania

Switch expression w C# - wzorce i pułapki dopasowania

Radosław Stępień • 18 października 2026
Ustawienia kompilacji w Visual Studio, gdzie widać opcję wyboru wersji języka C# 8.0 (beta) dla projektu SwitchCSharp8.

Spis treści

Masz metodę z kilkoma warunkami, a każdy z nich ma zwrócić inny status, komunikat albo obiekt? Wyrażenie switch w C# zwykle porządkuje taki wybór, choć przy wartościach null i niedopasowanych danych łatwo przeoczyć istotny przypadek.

Porównanie kodu C# z użyciem c# switch expression:

Najlepiej używać switch expression, gdy każda gałąź zwraca jedną wartość

  • Wynik zamiast instrukcji: każda ramka wyrażenia przypisuje lub zwraca wartość.
  • Kolejność ma znaczenie: wygrywa pierwsza pasująca gałąź, której warunek when jest prawdziwy.
  • Wzorzec odrzucający _: obsługuje pozostałe przypadki, ale może też zamaskować nieoczekiwane dane.
  • Wzorce dopasowania: wzorce typów, właściwości i zakresów pozwalają ograniczyć rozbudowane łańcuchy if.

Switch expression pasuje do wyboru jednej wartości

Najłatwiej rozpoznać dobre zastosowanie po lewej stronie przypisania: jeśli wynik ma być pojedynczą wartością, wyrażenie switch często czytelnie zastępuje łańcuch if. Wprowadzono je w C# 8; każda gałąź, nazywana ramieniem, ma wzorzec i wyrażenie wyniku.

static string GetLabel(int status) =>
    status switch
    {
        200 => "OK",
        404 => "Nie znaleziono",
        500 => "Błąd serwera",
        _   => "Nieznany status"
    };

W tym przykładzie każda ścieżka zwraca tekst, więc zapis można wprost wykorzystać w przypisaniu, instrukcji return albo jako część większego wyrażenia. Microsoft opisuje tę formę jako sposób na wybranie wartości na podstawie pierwszego pasującego wzorca w dokumentacji wyrażenia switch w C#.

Granica jest praktyczna: gdy gałąź ma wykonać kilka działań, zapisać log, zmienić stan i dopiero potem zakończyć metodę, zwykła instrukcja switch może być czytelniejsza. Uważam, że zwięzłość pomaga tylko wtedy, gdy odbiorca od razu widzi, skąd bierze się wynik.

Wzorce typów i właściwości zamieniają if na opis danych

Przy obiektach sama wartość często nie wystarcza. Potrzebujesz rozpoznać typ i sprawdzić jego właściwości, a wzorzec właściwości pozwala połączyć te testy bez osobnych warunków zagnieżdżonych.

static string DeliveryMessage(Delivery? delivery) =>
    delivery switch
    {
        null => "Brak dostawy",
        ExpressDelivery express =>
            $"Przesyłka ekspresowa: {express.TrackingCode}",
        StandardDelivery { Days: <= 2 } =>
            "Dostawa standardowa wkrótce",
        StandardDelivery =>
            "Dostawa standardowa",
        _ => "Inny rodzaj dostawy"
    };

ExpressDelivery express sprawdza typ i udostępnia dopasowany obiekt pod nazwą express. Z kolei StandardDelivery { Days: <= 2 } łączy rozpoznanie typu z warunkiem na właściwości. Takie dopasowanie bywa bardziej czytelne niż kilka poziomów if, gdy przypadki wynikają z kształtu danych.

Przy bardziej złożonym warunku możesz dodać when, czyli dodatkowy warunek logiczny po wzorcu. Przykładowo StandardDelivery delivery when delivery.Days > limit może rozdzielić zamówienia według czasu dostawy, ale gdy warunek da się zapisać bezpośrednio jako wzorzec właściwości, krótsza forma zwykle łatwiej się skanuje.

Zakresy i kolejność ramion rozstrzygają granice

Gdy wiele wartości liczbowych prowadzi do tych samych wyników, wzorce relacyjne zapisują przedziały bez powtarzania każdej liczby. Trzeba jednak patrzeć na kolejność od góry: pierwsze pasujące ramię kończy dopasowanie.

static string GetTemperatureLabel(int celsius) =>
    celsius switch
    {
        < 0 => "Mróz",
        0 => "Punkt zamarzania",
        > 0 and < 30 => "Umiarkowana temperatura",
        _ => "Upał"
    };

Wzorce relacyjne, takie jak < 0, oraz logiczne, takie jak > 0 and < 30, są częścią współczesnego dopasowania wzorców w C#. Jeśli wzorce nachodzą na siebie, bardziej szczegółowy warunek umieść przed ogólnym. Ramię _ umieszczone wcześniej przechwyci wszystko i kolejne gałęzie staną się nieosiągalne. Więcej o łączeniu zakresów opisuje dokumentacja Microsoft dotycząca wzorców relacyjnych i logicznych.

Wybór wzorca zależy też od wersji języka: wzorce relacyjne i logiczne pojawiły się w C# 9, a wzorce list w C# 11. Jeśli projekt ma starszą wersję języka, składnia może nie zostać rozpoznana, nawet gdy kod wygląda poprawnie na nowszym przykładzie.

Brakująca gałąź może ujawnić się dopiero podczas działania

Tu kryje się częsta pułapka: ostrzeżenie kompilatora nie daje gwarancji, że każda możliwa kombinacja wartości została obsłużona. Gdy żadne ramię nie pasuje, wyrażenie może zakończyć się wyjątkiem SwitchExpressionException, zamiast po cichu zwrócić wartość domyślną.

Sytuacja Możliwy skutek Co zrobić
Typ wejściowy dopuszcza null, ale nie ma ramienia null Niejasna obsługa pustego wejścia lub ostrzeżenie o niepełnym dopasowaniu Dodaj null => ... albo jawnie odrzuć null przed dopasowaniem
Enum ma nieoczekiwaną wartość liczbową Żadna z nazwanych wartości wyliczenia nie pasuje Dodaj _ z wynikiem awaryjnym albo rzuć wyjątek z czytelnym komunikatem
Ogólny wzorzec pojawia się przed szczegółowym Późniejsze ramię może być nieosiągalne Przenieś szeroki wzorzec na koniec i sprawdź ostrzeżenia kompilatora
Nowy typ dziedziczący nie pasuje do znanych typów Nieobsłużona gałąź podczas działania programu Przewidź zachowanie dla nieznanego typu albo jawnie zgłoś błąd

Przy enumie warto odróżnić dwie intencje. Jeśli każda nieznana wartość ma dostać bezpieczny, użyteczny wynik, ramię _ może być właściwe; jeśli nowa wartość oznacza błąd wymagający poprawki, lepszy będzie wyjątek niż etykieta „nieznany”. Microsoft zwraca uwagę, że kompilator ostrzega o wykrytych lukach i nakładających się wzorcach, ale nie potrafi dowieść kompletności dla każdej kombinacji.

Wybierz wyrażenie dla mapowania, instrukcję dla działań

Prosty test decyzyjny brzmi: czy każda gałąź ma dostarczyć jedną wartość? Jeśli tak, wyrażenie zwykle dobrze pasuje. Jeśli każda ścieżka wykonuje serię operacji lub ma kilka kroków, zostań przy instrukcji switch albo wydziel logikę do osobnych metod.

  • Mapowanie statusu na etykietę: użyj wyrażenia, bo każda wartość wejściowa daje jeden tekst.
  • Wybór implementacji według typu: zastosuj wzorce typów, gdy każda klasa wymaga innego wyniku.
  • Walidacja zakresu: użyj wzorców relacyjnych, jeśli przedziały są krótkie i nie nachodzą na siebie.
  • Obsługa skutków ubocznych: wybierz instrukcję, gdy gałąź zapisuje kilka zmian lub wykonuje wiele niezależnych działań.

Jeśli warunki zaczynają przypominać mały silnik reguł, zagnieżdżają się albo używają tych samych obliczeń w wielu ramionach, skrócenie zapisu może utrudnić utrzymanie kodu. Lepiej wyciągnąć obliczenie do nazwanej metody i zostawić w wyrażeniu sam wybór.

Dobry switch expression kończy się świadomym przypadkiem awaryjnym

Najczytelniejsze dopasowanie zaczyna się od przypadków szczegółowych, jawnie obejmuje null tam, gdzie może się pojawić, i kończy obsługą reszty. Ramię domyślne powinno wyrażać decyzję projektową, a nie służyć wyłącznie do uciszenia ostrzeżenia.

Gdy brakująca wartość jest normalna, zwróć sensowny wynik awaryjny. Gdy świadczy o błędzie danych albo nieaktualnym założeniu w kodzie, zgłoś problem. Wtedy zapis pozostaje krótki, a zachowanie programu nie zaskoczy cię dopiero w produkcji.

FAQ - Najczęstsze pytania

Wyrażenie switch sprawdza się wtedy, gdy każda gałąź ma zwrócić jedną wartość, na przykład etykietę statusu, komunikat albo obiekt. Gdy ramiona wykonują kilka działań, zapisują logi, zmieniają stan lub powodują inne skutki uboczne, czytelniejsza może być zwykła instrukcja switch albo osobna metoda.

Jeśli wejście może być puste, dodaj jawne ramię null => ... albo odrzuć null przed dopasowaniem. Dla nieznanej wartości enuma lub nowego typu możesz użyć ramienia _ z bezpiecznym wynikiem, ale jeśli taka wartość oznacza błąd danych, lepiej rzucić wyjątek z czytelnym komunikatem. Brak pasującego ramienia może zakończyć wyrażenie wyjątkiem SwitchExpressionException.

Dopasowanie kończy się na pierwszym ramieniu, którego wzorzec pasuje i którego warunek when jest prawdziwy. Dlatego szczegółowe wzorce należy umieszczać przed ogólnymi, a ramię _ zwykle na końcu. Wcześniejsze umieszczenie szerokiego wzorca może przechwycić wszystkie wartości i sprawić, że kolejne ramię będzie nieosiągalne.

Wyrażenia switch wprowadzono w C# 8. Wzorce relacyjne i logiczne, takie jak < 0 oraz > 0 and < 30, są dostępne od C# 9, a wzorce list od C# 11. W starszym projekcie nowsza składnia może nie zostać rozpoznana mimo poprawności przykładu dla aktualnej wersji języka.

Oceń artykuł

Ocena: 3.80 Liczba głosów: 5

Tagi

c#
wyrażenia switch
wzorce relacyjne
enumy
dopasowanie wzorców
Autor Radosław Stępień
Radosław Stępień
Dziedzina programowania i technologii rozwija się w zawrotnym tempie, co sprawia, że ciągłe śledzenie nowinek i głębokie zrozumienie procesów są kluczowe. Nazywam się Radosław Stępień i od 13 lat aktywnie działam w branży IT, dzieląc się swoją wiedzą i doświadczeniem. Moje zainteresowania skupiają się na praktycznych aspektach tworzenia oprogramowania, analizie trendów technologicznych oraz budowaniu satysfakcjonującej ścieżki kariery w tym dynamicznym sektorze. Staram się przedstawiać złożone zagadnienia w sposób przystępny, opierając się na sprawdzonych informacjach i własnych obserwacjach z wieloletniej pracy. Zależy mi na tym, aby treści, które tworzę, były nie tylko merytoryczne i aktualne, ale przede wszystkim pomocne dla każdego, kto chce zgłębić tajniki IT lub rozwijać się zawodowo.

Udostępnij artykuł

Napisz komentarz