Menu

Instrukcja switch w C#: case, break, default i wiele przypadków

Jak działa instrukcja switch w C#: switch na wartościach int, string, char i enum, dlaczego każdy case potrzebuje break (błąd CS0163), łączenie etykiet case dla kilku wartości, goto case, default, wielkość liter w stringach oraz etykiety case ze wzorcami i when.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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ę:

  • return opuszcza całą metodę, co w małej metodzie pomocniczej jest często najschludniejszą formą.
  • throw rzuca wyjątek dla wartości, która nigdy nie powinna wystąpić.
  • continue przechodzi do następnej iteracji, gdy switch jest wewnątrz pętli.
  • goto case i goto default celowo 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ściStałe przypadkiUwagi
int, long, byte i inne liczby całkowitetakklasyczny przypadek
chartakcase 'a':
stringtakporządkowo, z rozróżnianiem wielkości liter, null trafia do default
booltakdozwolone, ale if jest czytelniejszy
dowolny enumtakbez sprawdzania pełnego pokrycia
double, decimalod C# 7tylko stałe; zakresy wymagają wzorców relacyjnych z C# 9
dowolny inny typod C# 7przez 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.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ