Instrukcja switch
Gdy program ma wybrać jedną z wielu opcji na podstawie jednej wartości — numeru miesiąca, litery menu, kodu operacji — drabinka else if robi się długa i powtarzalna. Od tego jest switch: ta sama decyzja zapisana krócej i czytelniej.
Czego się dziś nauczysz
- Zamienisz długą drabinkę
else ifna instrukcjęswitch - Wyjaśnisz, dlaczego w C# nie wolno pominąć
break - Obsłużysz kilka wartości jednym blokiem dzięki etykietom zbiorczym
- Zapiszesz przypisanie krócej, używając wyrażenia
switch - Rozpoznasz sytuacje, w których
switchsię nie nadaje
Instrukcja switch
switch bierze jedną wartość i porównuje ją po kolei z etykietami case. Gdy znajdzie zgodną, wykonuje instrukcje spod niej.
switch (wyrazenie)
{
case wartosc1:
// instrukcje
break;
case wartosc2:
// instrukcje
break;
default:
// gdy nic nie pasowalo
break;
}
Porównaj te same reguły zapisane na dwa sposoby:
int dzien = 3;
if (dzien == 1) { Console.WriteLine("poniedzialek"); }
else if (dzien == 2) { Console.WriteLine("wtorek"); }
else if (dzien == 3) { Console.WriteLine("sroda"); }
else if (dzien == 4) { Console.WriteLine("czwartek"); }
else if (dzien == 5) { Console.WriteLine("piatek"); }
else { Console.WriteLine("weekend albo blad"); }
int dzien = 3;
switch (dzien)
{
case 1:
Console.WriteLine("poniedzialek");
break;
case 2:
Console.WriteLine("wtorek");
break;
case 3:
Console.WriteLine("sroda");
break;
case 4:
Console.WriteLine("czwartek");
break;
case 5:
Console.WriteLine("piatek");
break;
default:
Console.WriteLine("weekend albo blad");
break;
}
sroda
Co zyskujemy
Nazwa sprawdzanej zmiennej pojawia się raz, a nie w każdej linii. Dzięki temu nie da się przez pomyłkę porównać w jednej gałęzi innej zmiennej — a to zaskakująco częsty błąd w długich drabinkach. Widać też od razu, że wszystkie warunki dotyczą tej samej wartości.
Etykieta default
default odpowiada gałęzi else: wykona się, gdy żaden case nie pasował. Nie jest obowiązkowa — bez niej program po prostu nic nie zrobi — ale w programie, który dostaje dane od użytkownika, jej brak jest błędem. Ktoś zawsze wpisze coś, czego nie przewidziałeś.
Etykieta default nie musi stać na końcu (kompilator to dopuszcza), ale zawsze ją tam pisz — inaczej czytający musi jej szukać.
Dlaczego break jest obowiązkowy
Każda gałąź switch musi się jednoznacznie kończyć. Najczęściej robi to break, który znaczy „wychodzimy ze switch„. Pominięcie go to błąd kompilacji:
switch (ocena)
{
case 5:
Console.WriteLine("Bardzo dobry");
// BLAD CS0163: nie mozna "przelecec" z jednej etykiety do drugiej
case 4:
Console.WriteLine("Dobry");
break;
}
C# jest tu bezpieczniejszy niż C, C++ i Java
W tamtych językach brak break jest legalny i powoduje przelecenie do następnej gałęzi — program wypisałby „Bardzo dobry” i „Dobry”. To klasyczne źródło błędów, bo pomyłka wygląda jak poprawny kod. C# takiego zapisu po prostu nie skompiluje. Jeśli w cudzym kodzie w innym języku widzisz case bez break, przyjrzyj mu się uważnie.
Świadome przejście do innej gałęzi: goto case
Gdy naprawdę chcesz, żeby jedna gałąź przeszła do drugiej, C# każe napisać to wprost:
switch (poziom)
{
case "administrator":
Console.WriteLine("Moze zmieniac ustawienia.");
goto case "uzytkownik"; // administrator ma tez prawa uzytkownika
case "uzytkownik":
Console.WriteLine("Moze czytac dane.");
break;
default:
Console.WriteLine("Brak uprawnien.");
break;
}
Zapis jest poprawny, ale rzadko potrzebny — zwykle czytelniej jest powtórzyć jedną linię albo wywołać wspólną metodę. Wspominam o nim, żebyś go rozpoznał, a nie po to, żebyś go używał.
Inne zakończenia gałęzi
Gałąź może się też skończyć instrukcją return (gdy switch stoi w metodzie — blok o funkcjach) albo throw (przy zgłaszaniu błędu — blok o wyjątkach). W programach z tej lekcji zawsze będzie to break.
Etykiety zbiorcze
Kilka etykiet case może prowadzić do jednego bloku instrukcji. To nie jest „przelecenie” — pusta etykieta niczego nie wykonuje, więc jest bezpieczna i nie wymaga break:
int miesiac = 7;
switch (miesiac)
{
case 12:
case 1:
case 2:
Console.WriteLine("zima");
break;
case 3:
case 4:
case 5:
Console.WriteLine("wiosna");
break;
case 6:
case 7:
case 8:
Console.WriteLine("lato");
break;
case 9:
case 10:
case 11:
Console.WriteLine("jesien");
break;
default:
Console.WriteLine("Numer miesiaca musi byc z zakresu 1-12.");
break;
}
lato
Ten sam zapis drabinką else if wymagałby warunków w rodzaju miesiac == 12 || miesiac == 1 || miesiac == 2 — dłuższych i łatwiejszych do pomylenia.
Nowszy, krótszy zapis
Od C# 9 kilka wartości można wypisać w jednej etykiecie po przecinku: case 12 or 1 or 2:. Działa też zakres: case >= 6 and <= 8:. Wrócimy do tego w sekcji o wzorcach.
Na czym switch działa, a na czym nie
Etykieta case musi być stałą — wartością znaną w chwili kompilacji. Nie może to być zmienna ani wynik obliczenia.
| Typ | Uwagi |
|---|---|
int, long, short, byte | Typowe zastosowanie: numer opcji, kod błędu, numer miesiąca. |
char | Litera menu, znak operatora. Etykieta w apostrofach: case 'a':. |
string | Działa i jest wygodne, ale rozróżnia wielkość liter — patrz ostrzeżenie niżej. |
bool | Dopuszczalne, ale bez sensu — dwie możliwości to zadanie dla if/else. |
enum | Najlepsze połączenie. Typ wyliczeniowy poznamy w bloku o C# nowoczesnym. |
double, float, decimal | Technicznie dozwolone, praktycznie nigdy. Porównywanie liczb ułamkowych z konkretną wartością jest zawodne — po to jest if z tolerancją. |
int prog = 5;
switch (ocena)
{
case prog: // BLAD CS0150: oczekiwano stalej
break;
}
const int Prog = 5;
switch (ocena)
{
case Prog: // OK - stala jest znana przy kompilacji
break;
}
switch na napisach rozróżnia wielkość liter
case "tak": nie złapie odpowiedzi „TAK” ani „Tak”. W przeciwieństwie do if nie da się tu podać StringComparison. Rozwiązanie: ujednolić wartość przed wejściem do switch.
string odp = (Console.ReadLine() ?? "").Trim().ToLower();
switch (odp)
{
case "tak":
case "t":
Console.WriteLine("Potwierdzono.");
break;
case "nie":
case "n":
Console.WriteLine("Anulowano.");
break;
default:
Console.WriteLine("Nie rozumiem odpowiedzi.");
break;
}
Trim() usuwa spacje z początku i końca, ToLower() zamienia wszystko na małe litery. Po tych dwóch krokach „ TAK „, „Tak” i „tak” trafiają w tę samą etykietę.
Wyrażenie switch
Bardzo często cały switch służy tylko do przypisania jednej wartości. Od C# 8 jest na to krótszy zapis — wyrażenie switch. Tak jak operator ?: z poprzedniej lekcji, zwraca wartość:
string nazwa;
switch (ocena)
{
case 6: nazwa = "celujacy"; break;
case 5: nazwa = "bardzo dobry"; break;
case 4: nazwa = "dobry"; break;
case 3: nazwa = "dostateczny"; break;
case 2: nazwa = "dopuszczajacy"; break;
case 1: nazwa = "niedostateczny"; break;
default: nazwa = "nieznana ocena"; break;
}
string nazwa = ocena switch
{
6 => "celujacy",
5 => "bardzo dobry",
4 => "dobry",
3 => "dostateczny",
2 => "dopuszczajacy",
1 => "niedostateczny",
_ => "nieznana ocena"
};
| Różnica | Instrukcja / wyrażenie |
|---|---|
Słowo switch | Przed nawiasem / po nazwie zmiennej |
| Etykiety | case 6: / samo 6 => |
| Rozdzielenie gałęzi | break; / przecinek |
| Przypadek domyślny | default: / znak podkreślenia _ |
| Zakończenie | Klamra / klamra i średnik |
| Co robi | Wykonuje instrukcje / zwraca wartość |
Średnik po klamrze zamykającej
Wyrażenie switch jest częścią instrukcji przypisania, więc kończy się średnikiem: };. To najczęstsza pomyłka przy pierwszym użyciu — po instrukcji switch średnika nie ma.
Znak _ nie jest opcjonalny
W wyrażeniu switch gałąź _ jest praktycznie obowiązkowa. Bez niej kompilator ostrzega, że nie wszystkie wartości są obsłużone, a gdy program natrafi na nieprzewidziany przypadek, przerwie działanie z błędem SwitchExpressionException. W instrukcji switch brak default oznacza tylko „nic nie rób”.
Wyrażenie switch można wstawić wszędzie tam, gdzie potrzebna jest wartość — także wprost do Console.WriteLine:
int liczbaPlikow = 1;
string slowo = liczbaPlikow switch
{
1 => "plik",
2 or 3 or 4 => "pliki",
_ => "plikow"
};
Console.WriteLine($"Znaleziono {liczbaPlikow} {slowo}");
Znaleziono 1 plik
Polska odmiana jest trudniejsza, niż wygląda
Powyższy zapis działa dla liczb 0–21, ale już 22 wymaga formy „pliki”, a nie „plikow”. Pełna reguła zależy od dwóch ostatnich cyfr i sama nadaje się na zadanie ze wzorców. Zapamiętaj z tego przykładu jedno: wyrażenie switch można przypisać do zmiennej i użyć w interpolacji jak każdej innej wartości.
Wzorce: switch na zakresach
Nowsze wersje C# pozwalają w etykiecie napisać nie tylko konkretną wartość, ale i warunek. Dzięki temu switch radzi sobie z progami, które dotąd wymagały drabinki else if:
int procent = 82;
string ocena = procent switch
{
< 0 or > 100 => "wynik poza zakresem",
>= 98 => "6",
>= 90 => "5",
>= 75 => "4",
>= 50 => "3",
>= 30 => "2",
_ => "1"
};
Console.WriteLine($"Wynik {procent}% to ocena {ocena}.");
Wynik 82% to ocena 4.
| Wzorzec | Znaczenie |
|---|---|
>= 90 | wartość większa lub równa 90 — działają wszystkie operatory porównania |
1 or 2 or 3 | jedna z wypisanych wartości; zastępuje etykiety zbiorcze |
>= 6 and <= 8 | przedział — oba warunki naraz |
not 0 | cokolwiek poza zerem |
_ | wszystko pozostałe |
Kolejność nadal decyduje
Wzorce są sprawdzane od góry, dokładnie jak gałęzie else if. Gdyby >= 50 stało przed >= 90, wynik 95% dostałby ocenę 3. Zaleta jest inna: zapis jest krótszy i widać w nim całą tabelę progów naraz.
Ten sam mechanizm działa też w zwykłej instrukcji switch — słowem kluczowym when:
switch (wiek)
{
case int w when w < 0:
Console.WriteLine("Wiek nie moze byc ujemny.");
break;
case 0:
Console.WriteLine("Noworodek.");
break;
case int w when w < 18:
Console.WriteLine("Osoba niepelnoletnia.");
break;
default:
Console.WriteLine("Osoba pelnoletnia.");
break;
}
To pełnoprawne rozwiązanie, ale w praktyce w takich sytuacjach częściej sięgamy po drabinkę else if albo po wyrażenie switch ze wzorcami — oba są krótsze.
Kiedy switch, a kiedy if
Wybierz switch, gdy… | Wybierz if, gdy… |
|---|---|
| decyzja zależy od jednej wartości | warunek łączy kilka zmiennych (wiek >= 18 && maPrawoJazdy) |
| możliwości jest co najmniej trzy | możliwości są dwie |
| porównujesz z konkretnymi wartościami | porównujesz liczby ułamkowe albo używasz tolerancji |
| lista opcji jest zamknięta (miesiące, menu, kody) | warunek to złożone wyrażenie logiczne |
W praktyce: jeśli piszesz else if po raz trzeci i za każdym razem porównujesz tę samą zmienną z inną wartością — to jest switch.
Użyte w tej lekcji metody
| Nazwa | Zwraca | Działanie |
|---|---|---|
s.Trim() | string |
Zwraca nowy napis bez spacji, tabulatorów i znaków końca linii z początku i końca. Środek zostaje nietknięty. Warianty: TrimStart(), TrimEnd(). |
s.ToLower() / s.ToUpper() | string |
Zwraca napis zamieniony w całości na małe / wielkie litery. Oryginał zostaje bez zmian — napisy w C# są niezmienne. |
Math.Abs(x) | liczba | Wartość bezwzględna. W kalkulatorze służy do sprawdzenia, czy dzielnik nie jest (prawie) zerem. |
double.IsNaN(x) | bool |
Czy wartość to „nie liczba” — wynik operacji bez sensu, np. 0.0 / 0.0 albo pierwiastek z liczby ujemnej. |
Enum, DateTime.Now.DayOfWeek | — | Wyliczenia i typ DateTime dają wartości, na których switch sprawdza się najlepiej. Poznamy je w dalszych blokach kursu. |
Kalkulator z menu
Klasyczne zadanie egzaminacyjne: program wczytuje dwie liczby i znak działania, po czym wypisuje wynik. To dokładnie ten kształt problemu, do którego switch został stworzony — jedna wartość, kilka z góry znanych możliwości.
Console.WriteLine("=== KALKULATOR ===");
Console.WriteLine();
Console.Write("Pierwsza liczba: ");
bool ok1 = double.TryParse(Console.ReadLine(), out double a);
Console.Write("Znak dzialania (+ - * /): ");
string wpis = (Console.ReadLine() ?? "").Trim();
Console.Write("Druga liczba: ");
bool ok2 = double.TryParse(Console.ReadLine(), out double b);
Console.WriteLine();
if (!ok1 || !ok2)
{
Console.WriteLine("Obie wartosci musza byc liczbami.");
}
else if (wpis.Length != 1)
{
Console.WriteLine("Podaj dokladnie jeden znak dzialania.");
}
else
{
char dzialanie = wpis[0];
double wynik = 0;
bool policzono = true;
switch (dzialanie)
{
case '+':
wynik = a + b;
break;
case '-':
wynik = a - b;
break;
case '*':
case 'x': // wielu uzytkownikow pisze x zamiast gwiazdki
wynik = a * b;
break;
case '/':
if (Math.Abs(b) < 0.000001)
{
Console.WriteLine("Nie dziel przez zero.");
policzono = false;
}
else
{
wynik = a / b;
}
break;
default:
Console.WriteLine($"Nieznane dzialanie: {dzialanie}");
policzono = false;
break;
}
if (policzono)
{
Console.WriteLine($"{a} {dzialanie} {b} = {wynik:F4}");
}
}
=== KALKULATOR ===
Pierwsza liczba: 7,5
Znak dzialania (+ - * /): /
Druga liczba: 2
7,5 / 2 = 3,7500
Omówienie
| Fragment | Co robi i dlaczego tak |
|---|---|
wpis.Length != 1 |
Zanim wyciągniemy pierwszy znak, sprawdzamy, że jakiś w ogóle jest. Bez tego wpis[0] na pustym napisie przerwałby program błędem IndexOutOfRangeException. |
char dzialanie = wpis[0]; |
Kwadratowe nawiasy dają pojedynczy znak napisu. switch na char jest wygodniejszy niż na string — etykiety zapisujemy w apostrofach. |
double wynik = 0; |
Wartość początkowa jest konieczna: kompilator nie wie, że któraś gałąź switch na pewno przypisze wynik, więc bez niej zgłosiłby CS0165. |
bool policzono = true; |
Flaga — zmienna logiczna zapamiętująca, czy coś się udało. Dwie gałęzie ustawiają ją na false, a wypisanie wyniku dzieje się po switch, tylko raz. Bez flagi trzeba by powtórzyć tę samą linię w każdej gałęzi. |
case '*': case 'x': |
Etykiety zbiorcze. Pusta etykieta '*' niczego nie wykonuje, więc nie potrzebuje break — to nie jest zabronione „przelecenie”. |
Math.Abs(b) < 0.000001 |
Sprawdzenie dzielnika. Nie piszemy b == 0, bo b jest typu double i po wcześniejszych obliczeniach mogłoby wynosić 0,0000000001 — technicznie nie zero, a dzielenie i tak dałoby bezużyteczny wynik. |
if wewnątrz case |
Gałąź switch to zwykły blok instrukcji — może w nim stać if, obliczenia, wszystko. break musi być na końcu całej gałęzi, nie w każdej odnodze if. |
Wersja krótsza — ale nie zawsze lepsza
Samo działanie da się zapisać wyrażeniem switch w czterech liniach. Tutaj byłoby to trudniejsze, bo dzielenie przez zero wymaga własnego komunikatu i pominięcia wypisania wyniku. Wyrażenie switch jest dla przypadków, w których każda gałąź daje wartość; gdy któraś ma zrobić coś innego, wracamy do instrukcji.
Na czym się najczęściej potykamy
| Błąd | Co się dzieje i jak poprawić |
|---|---|
brak break |
CS0163: Control cannot fall through from one case label to another. Każda gałąź z instrukcjami musi kończyć się break (albo return, throw, goto case). |
| zmienna jako etykieta | CS0150: A constant value is expected. Etykieta musi być literałem albo stałą const. Jeśli potrzebujesz zmiennej — to zadanie dla if. |
brak średnika po wyrażeniu switch |
CS1002. Wyrażenie kończy się };, instrukcja — samą klamrą. |
case "tak" na odpowiedzi „TAK” |
Nie zadziała — switch na napisach rozróżnia wielkość liter i nie przyjmuje StringComparison. Ujednolić przed: .Trim().ToLower(). |
brak default |
W instrukcji: program po cichu nic nie zrobi i nie dowiesz się dlaczego. W wyrażeniu: ostrzeżenie przy kompilacji i wyjątek SwitchExpressionException przy nieprzewidzianej wartości. |
| deklaracja zmiennej w gałęzi | Cały switch to jeden zasięg — nie można zadeklarować zmiennej o tej samej nazwie w dwóch gałęziach (CS0128). Zadeklaruj ją przed switch albo obejmij gałąź własnymi klamrami. |
| dwie identyczne etykiety | CS0152 — ta sama wartość nie może pojawić się w dwóch case. Przy dużej liczbie gałęzi łatwo o taką pomyłkę i dobrze, że kompilator ją łapie. |
switch na kilku zmiennych |
switch patrzy na jedną wartość. Warunek typu „wiek i posiadanie prawa jazdy” zapisz instrukcją if. |
| zła kolejność wzorców | Przy etykietach z porównaniami (>= 90) obowiązuje ta sama reguła co w drabince: od najostrzejszego warunku. Kompilator ostrzeże tylko wtedy, gdy któraś gałąź jest całkiem nieosiągalna. |
Zadania
Wczytaj numer dnia tygodnia (1–7) i wypisz jego nazwę. Dla 6 i 7 dopisz „ (weekend)”. Numery spoza zakresu odrzuć.
pokaż rozwiązanie
Console.Write("Numer dnia (1-7): ");
bool ok = int.TryParse(Console.ReadLine(), out int dzien);
if (!ok)
{
Console.WriteLine("Podaj liczbe.");
}
else
{
switch (dzien)
{
case 1: Console.WriteLine("poniedzialek"); break;
case 2: Console.WriteLine("wtorek"); break;
case 3: Console.WriteLine("sroda"); break;
case 4: Console.WriteLine("czwartek"); break;
case 5: Console.WriteLine("piatek"); break;
case 6: Console.WriteLine("sobota (weekend)"); break;
case 7: Console.WriteLine("niedziela (weekend)"); break;
default: Console.WriteLine("Numer musi byc z zakresu 1-7."); break;
}
}
Numer dnia (1-7): 6
sobota (weekend)
Przy jednolinijkowych gałęziach cały case mieści się w jednej linii — czyta się to jak tabelę. Sprawdzenie TryParse zostaje na zewnątrz, bo switch nie ma jak obsłużyć „to w ogóle nie była liczba”.
Wczytaj numer miesiąca i rok, po czym wypisz liczbę dni w tym miesiącu. Luty ma 28 dni, a w roku przestępnym 29. Wykorzystaj etykiety zbiorcze.
pokaż rozwiązanie
Console.Write("Numer miesiaca (1-12): ");
bool okM = int.TryParse(Console.ReadLine(), out int miesiac);
Console.Write("Rok: ");
bool okR = int.TryParse(Console.ReadLine(), out int rok);
if (!okM || !okR || rok < 1)
{
Console.WriteLine("Podaj poprawny miesiac i rok.");
}
else
{
int dni;
switch (miesiac)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
dni = 31;
break;
case 4:
case 6:
case 9:
case 11:
dni = 30;
break;
case 2:
dni = DateTime.IsLeapYear(rok) ? 29 : 28;
break;
default:
dni = 0;
break;
}
if (dni == 0)
{
Console.WriteLine("Numer miesiaca musi byc z zakresu 1-12.");
}
else
{
Console.WriteLine($"Miesiac {miesiac} w roku {rok} ma {dni} dni.");
}
}
Numer miesiaca (1-12): 2
Rok: 2024
Miesiac 2 w roku 2024 ma 29 dni.
Etykiety zbiorcze zastępują tu warunek z sześcioma operatorami ||. Zwróć uwagę, że gałąź lutego zawiera operator warunkowy — gałąź switch może robić dowolne obliczenia.
Wartość 0 pełni rolę znacznika „nie ustalono”. W prawdziwym programie lepszym rozwiązaniem byłaby osobna flaga albo od razu wypisanie komunikatu w gałęzi default — tu zostawiam ten zapis, bo pokazuje typową konstrukcję, którą spotkasz w cudzym kodzie.
Istnieje też gotowa metoda: DateTime.DaysInMonth(rok, miesiac). W programie użytkowym należałoby jej użyć — to zadanie jest ćwiczeniem ze switch.
Wyświetl menu z czterema opcjami (dodaj, usuń, wypisz, koniec), wczytaj wybór jako literę i wypisz komunikat o wybranej operacji. Program ma przyjmować zarówno małe, jak i wielkie litery.
pokaż rozwiązanie
Console.WriteLine("=== MENU ===");
Console.WriteLine(" d - dodaj pozycje");
Console.WriteLine(" u - usun pozycje");
Console.WriteLine(" w - wypisz wszystkie");
Console.WriteLine(" k - koniec");
Console.WriteLine();
Console.Write("Twoj wybor: ");
string wpis = (Console.ReadLine() ?? "").Trim().ToLower();
if (wpis.Length == 0)
{
Console.WriteLine("Nic nie wybrano.");
}
else
{
switch (wpis[0])
{
case 'd':
Console.WriteLine("Dodawanie nowej pozycji...");
break;
case 'u':
Console.WriteLine("Usuwanie pozycji...");
break;
case 'w':
Console.WriteLine("Lista pozycji:");
break;
case 'k':
Console.WriteLine("Do zobaczenia!");
break;
default:
Console.WriteLine($"Nieznana opcja: '{wpis[0]}'");
break;
}
}
=== MENU ===
d - dodaj pozycje
u - usun pozycje
w - wypisz wszystkie
k - koniec
Twoj wybor: W
Lista pozycji:
ToLower() wywołane przed switch załatwia obsługę wielkich liter raz dla wszystkich gałęzi. Alternatywa — dopisanie case 'D': obok każdego case 'd': — podwoiłaby liczbę etykiet.
Kolejność Trim(), potem ToLower() nie ma tu znaczenia, ale warto wyrobić sobie nawyk: najpierw obcinamy śmieci, potem ujednolicamy.
Prawdziwe menu powtarza się do wyboru opcji „koniec”. Dopiszemy tę pętlę w następnej lekcji.
Napisz program, który dla numeru miesiąca zwraca porę roku — wyrażeniem switch, w jednej instrukcji przypisania. Użyj słowa or zamiast osobnych etykiet.
pokaż rozwiązanie
Console.Write("Numer miesiaca (1-12): ");
bool ok = int.TryParse(Console.ReadLine(), out int miesiac);
string pora = miesiac switch
{
12 or 1 or 2 => "zima",
3 or 4 or 5 => "wiosna",
6 or 7 or 8 => "lato",
9 or 10 or 11 => "jesien",
_ => "nieznany miesiac"
};
Console.WriteLine(ok ? $"Miesiac {miesiac} to {pora}." : "Podaj liczbe.");
Numer miesiaca (1-12): 11
Miesiac 11 to jesien.
Piętnaście linii instrukcji switch zmieściło się w siedmiu. Gałąź _ obsługuje zarówno numery spoza zakresu, jak i zero, które TryParse zostawia po nieudanej konwersji.
Zwróć uwagę na średnik po klamrze zamykającej — to część instrukcji przypisania.
Wczytaj ocenę (1–6) i wypisz jej nazwę słowną oraz informację, czy jest pozytywna. Nazwę ustal wyrażeniem switch na wartościach, a ocenę pozytywną — wyrażeniem switch ze wzorcem porównania.
pokaż rozwiązanie
Console.Write("Ocena (1-6): ");
bool ok = int.TryParse(Console.ReadLine(), out int ocena);
if (!ok)
{
Console.WriteLine("Podaj liczbe calkowita.");
}
else
{
string nazwa = ocena switch
{
6 => "celujacy",
5 => "bardzo dobry",
4 => "dobry",
3 => "dostateczny",
2 => "dopuszczajacy",
1 => "niedostateczny",
_ => "ocena spoza skali"
};
string status = ocena switch
{
>= 2 and <= 6 => "pozytywna",
1 => "negatywna",
_ => "nieokreslona"
};
Console.WriteLine($"{ocena} - {nazwa} ({status})");
}
Ocena (1-6): 4
4 - dobry (pozytywna)
Wzorzec >= 2 and <= 6 zamyka przedział z obu stron — bez górnej granicy ocena 9 zostałaby uznana za pozytywną. To ta sama pułapka co w drabince else if, tylko zapisana krócej.
Dwa niezależne wyrażenia switch na tej samej zmiennej to nie marnotrawstwo: każde odpowiada na inne pytanie i każde da się zmienić osobno.
Program wczytuje wartość oraz jednostkę źródłową i docelową (km, m, cm, mm). Przelicz wartość między jednostkami. Wskazówka: zamiast pisać wszystkie 16 kombinacji, przelicz najpierw na metry, a potem z metrów na jednostkę docelową.
pokaż rozwiązanie
Console.Write("Wartosc: ");
bool ok = double.TryParse(Console.ReadLine(), out double wartosc);
Console.Write("Jednostka zrodlowa (km/m/cm/mm): ");
string zJednostki = (Console.ReadLine() ?? "").Trim().ToLower();
Console.Write("Jednostka docelowa (km/m/cm/mm): ");
string naJednostke = (Console.ReadLine() ?? "").Trim().ToLower();
// ile metrow ma jedna jednostka; 0 oznacza "nie znam takiej"
double wspZrodla = zJednostki switch
{
"km" => 1000.0,
"m" => 1.0,
"cm" => 0.01,
"mm" => 0.001,
_ => 0.0
};
double wspCelu = naJednostke switch
{
"km" => 1000.0,
"m" => 1.0,
"cm" => 0.01,
"mm" => 0.001,
_ => 0.0
};
Console.WriteLine();
if (!ok)
{
Console.WriteLine("Wartosc musi byc liczba.");
}
else if (wspZrodla == 0 || wspCelu == 0)
{
Console.WriteLine("Obslugiwane jednostki to: km, m, cm, mm.");
}
else
{
double wMetrach = wartosc * wspZrodla;
double wynik = wMetrach / wspCelu;
Console.WriteLine($"{wartosc} {zJednostki} = {wynik} {naJednostke}");
}
Wartosc: 2,5
Jednostka zrodlowa (km/m/cm/mm): km
Jednostka docelowa (km/m/cm/mm): cm
2,5 km = 250000 cm
To jest najważniejszy pomysł w tym zadaniu. Zamiast szesnastu gałęzi (każda jednostka z każdą) mamy dwie listy po cztery pozycje: „ile metrów ma jedna sztuka tej jednostki”. Przeliczenie sprowadza się do mnożenia i dzielenia. Gdy dojdzie piąta jednostka, dopisujesz dwie linie zamiast dziewięciu.
Wartość 0 w gałęzi _ pełni rolę znacznika „nieznana jednostka” — bezpiecznie, bo żadna prawdziwa jednostka nie ma współczynnika zero. Sprawdzamy go przed dzieleniem.
Oba wyrażenia switch liczą się zawsze, nawet gdy dane były błędne. To bez znaczenia — nic nie kosztują i nie mogą się wywalić. Sprawdzenie poprawności jest tam, gdzie trzeba: przed użyciem wyników.
Sprawdź sam: 500 cm na metry da 5, a 1500 m na km da 1,5.
Co trzeba zapamiętać
switchporównuje jedną wartość z listą stałych — nazwa zmiennej pojawia się tylko raz.- W C#
breakjest obowiązkowy; brak przejścia między gałęziami to błądCS0163, a nie ciche „przelecenie” jak w C czy Javie. - Kilka etykiet
casenad jednym blokiem to etykiety zbiorcze — onebreaknie potrzebują. - Etykieta musi być stałą; zmienna daje
CS0150. switchna napisach rozróżnia wielkość liter — ujednolicaj wcześniej:.Trim().ToLower().- Wyrażenie
switch(zmienna switch { 1 => ..., _ => ... };) zwraca wartość i kończy się średnikiem. - Wzorce (
>= 90,1 or 2,>= 6 and <= 8) pozwalają obsłużyć progi i zakresy; kolejność wciąż decyduje. default(lub_) pisz zawsze — użytkownik wpisze coś, czego nie przewidziałeś.- Warunek na kilku zmiennych to zadanie dla
if, nie dlaswitch.