Instrukcja switch porównuje jedną wartość z listą stałych etykiet case i wykonuje pasującą sekcję. W C# każda sekcja musi kończyć się break (albo inną instrukcją, która ją opuszcza), więc jeden przypadek nigdy nie przejdzie po cichu do następnego.
Wynik:
200: OK
302: redirect
404: not found
500: unhandled status
Wartość w nawiasach jest obliczana raz i porównywana z każdą etykietą. default wykonuje się, gdy nic nie pasuje; jest opcjonalny i może stać w dowolnym miejscu listy, choć zwykle jest na końcu. Jeśli nic nie pasuje i nie ma default, cały switch nic nie robi.
Każda sekcja potrzebuje break
W C, C++, Javie i JavaScripcie zapomniane break sprawia, że wykonanie zjeżdża do następnego przypadku. C# zamienia ten błąd w błąd kompilacji:
switch (level)
{
case 1:
Console.WriteLine("read");
// error CS0163: Control cannot fall through from one case label ('case 1:') to another
case 2:
Console.WriteLine("write");
break;
}
Zasada brzmi: koniec listy instrukcji sekcji nie może być osiągalny. Zwykle spełnia się ją przez break, ale działa wszystko, co opuszcza sekcję:
returnopuszcza całą metodę, co w małej metodzie pomocniczej jest często najschludniejszą formą.throwrzuca wyjątek dla wartości, która nigdy nie powinna wystąpić.continueprzechodzi do następnej iteracji, gdyswitchjest wewnątrz pętli.goto caseigoto defaultcelowo skaczą do innej sekcji.
Ostatnia sekcja nie jest wyjątkiem. default: na dole bez break kończy się błędem CS8070, Control cannot fall out of switch from final case label ('default:').
Kilka wartości w jednym case
Etykiety bez instrukcji między nimi współdzielą sekcję poniżej. To jedyny rodzaj przejścia, na który C# pozwala, bo pusta etykieta nie może ukryć żadnego kodu:
Wynik:
'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit
Każda sekcja kończy się tu return, więc break nie jest potrzebny (break po return byłby nieosiągalnym kodem). Dwie etykiety z tą samą wartością to błąd, CS0152 The switch statement contains multiple cases with the label value '1', a każda etykieta musi być stałą znaną w czasie kompilacji: zmienna w case kończy się błędem "a constant value is expected".
Switch na stringu
Stringi to po liczbach całkowitych najczęstsza wartość w switch. Porównanie jest porządkowe i rozróżnia wielkość liter, więc przed switch znormalizuj wejście:
Wynik:
Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)
Bez ToLowerInvariant "STOP" trafiłoby do default. Switch na wartości null jest dozwolony: nie pasuje do żadnej stałej stringa i trafia do default, więc sam switch nigdy nie rzuca wyjątku.
Switch na enum
Typy wyliczeniowe to coś, do czego switch stworzono: zamknięty zbiór nazwanych wartości.
Wynik:
Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42
Kompilator nie sprawdza, czy instrukcja switch pokrywa każdy element enum, a zmienna enum może przechowywać dowolną liczbę całkowitą typu bazowego ((OrderStatus)42 jest dozwolone). default, który rzuca wyjątek, zamienia zapomniany przypadek w natychmiastowy, oczywisty błąd zamiast cichego braku działania.
goto case i goto default
Gdy jeden przypadek naprawdę powinien przejść do innego, C# każe ci zapisać skok:
Wynik:
admin:
manage users
edit posts
read posts
editor:
edit posts
read posts
guest:
read posts
goto case przyjmuje stałą, która musi pasować do jednej z etykiet w tym samym switch. W prawdziwym kodzie jest rzadkie: wspólne zachowanie jest zwykle czytelniejsze jako metoda wywoływana przez kilka przypadków. Nadal jest to jednak odpowiedź na pytanie, jak przejść dalej w C#.
Zmienne wewnątrz case
Cały blok switch to jeden zakres, więc dwa przypadki nie mogą zadeklarować zmiennej o tej samej nazwie:
switch (shape)
{
case "square":
int area = side * side;
break;
case "rectangle":
int area = width * height; // error CS0128: A local variable or function named 'area' is already defined in this scope
break;
}
Otocz ciało każdej sekcji nawiasami klamrowymi, żeby dać jej własny zakres: case "square": { int area = side * side; ... break; }.
break wewnątrz pętli
break w case opuszcza switch, a nie otaczającą go pętlę. continue działa odwrotnie: switch nie jest pętlą, więc continue przechodzi do następnej iteracji pętli.
Wynik:
end marker found
Total: 134
Pętla działała dalej po "end" i dodała 99. Żeby zatrzymać pętlę z wnętrza switch, ustaw flagę i sprawdź ją po switch albo przenieś pętlę do metody i użyj return. Strona o break i continue porównuje te możliwości.
Etykiety case ze wzorcami i when
Od C# 7.0 etykieta case może być wzorcem zamiast stałej: może sprawdzać typ wartości i dodawać warunek when. Ta forma jest częsta we współczesnym kodzie C#:
static string Describe(object value)
{
switch (value)
{
case null:
return "nothing";
case int n when n < 0:
return $"negative int {n}";
case int n:
return $"int {n}";
case string s when s.Length == 0:
return "empty string";
case string s:
return $"string \"{s}\"";
default:
return $"other: {value.GetType().Name}";
}
}
Przy wzorcach kolejność ma znaczenie: wygrywa pierwszy pasujący przypadek, a kompilator zgłasza błąd, gdy do przypadku nigdy nie da się dojść, bo wcześniejszy już go pokrywa (umieszczenie case int n: nad case int n when n < 0: kończy się błędem). Klauzula when działa też na stałych przypadkach: case 0 when isAdmin:. C# 9 dodał wzorce relacyjne i logiczne, więc case może brzmieć case >= 90: albo case 'a' or 'e' or 'i':.
W C# 7 bez wzorców to samo rozdzielanie według typu to łańcuch if ze wzorcem typu is:
Wynik:
negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing
Gdy każda gałąź switch tylko oblicza wartość, wyrażenie switch jest jeszcze krótsze: var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };. Strona o wyrażeniu switch omawia jego składnię, wzorce i zasady pełnego pokrycia.
Na czym można robić switch
| Typ wartości | Stałe przypadki | Uwagi |
|---|---|---|
int, long, byte i inne liczby całkowite | tak | klasyczny przypadek |
char | tak | case 'a': |
string | tak | porządkowo, z rozróżnianiem wielkości liter, null trafia do default |
bool | tak | dozwolone, ale if jest czytelniejszy |
dowolny enum | tak | bez sprawdzania pełnego pokrycia |
double, decimal | od C# 7 | tylko stałe; zakresy wymagają wzorców relacyjnych z C# 9 |
| dowolny inny typ | od C# 7 | przez wzorce typów, case Circle c: |
Najczęściej zadawane pytania
Czy break jest wymagany w switch w C#?
Każda sekcja case musi kończyć się instrukcją, która ją opuszcza: zwykle break, ale liczą się też return, throw, continue (wewnątrz pętli) albo goto case. Przejście z końca sekcji do następnej to błąd kompilacji, CS0163 Control cannot fall through from one case label ('case 1:') to another, i dotyczy to także ostatniej sekcji.
Jak obsłużyć kilka wartości w jednym case w C#?
Ułóż etykiety jedna pod drugą, bez niczego między nimi: case "sat": case "sun": Console.WriteLine("weekend"); break;. Puste etykiety case mogą przechodzić do następnej etykiety; tylko sekcja zawierająca instrukcje musi kończyć się break. W C# 9 i nowszych case ze wzorcem może też brzmieć case 1 or 2 or 3:.
Czy switch na stringu w C# rozróżnia wielkość liter?
Tak. Przypadki stringów są porównywane porządkowo, z rozróżnianiem wielkości liter, więc "Yes" nie pasuje do case "yes":. Najpierw znormalizuj wartość przez switch (input.Trim().ToLowerInvariant()). String null nie pasuje do żadnego stałego przypadku i trafia do default.
Czy switch w C# może przechodzić dalej jak w C albo Javie?
Nie niejawnie. Jeśli chcesz, żeby jeden case przeszedł do innego, zapisz to przez goto case <value>; albo goto default;. Dzięki temu każde przejście jest widoczne w kodzie i dlatego C# zabrania cichej wersji.
Jaka jest różnica między instrukcją switch a wyrażeniem switch w C#?
Instrukcja switch wykonuje instrukcje i potrzebuje case, break i default. Wyrażenie switch (C# 8 i nowsze) daje wartość: var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };. Używaj wyrażenia, gdy każda gałąź tylko oblicza wynik.