Menu

break, continue i goto w C#: wychodzenie z pętli i pętli zagnieżdżonych

Jak działają break, continue i goto w C#: wcześniejsze zakończenie pętli, pominięcie iteracji, break wewnątrz switch, trzy sposoby wyjścia z pętli zagnieżdżonych (flaga, goto albo metoda z return) oraz błąd nieskończonej pętli przez continue w while.

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

break wcześniej kończy pętlę. continue pomija resztę bieżącej iteracji i przechodzi do następnej. Obie instrukcje dotyczą najbardziej wewnętrznej otaczającej pętli i działają tak samo w for, foreach, while i do while.

Wynik:

process order of 45
skip invalid total 0
process order of 120
skip invalid total -5
process order of 80
test order found, stopping
after the loop

continue przeskoczyło linię przetwarzania dla 0 i -5. break zatrzymało pętlę na 999, więc do 60 pętla nigdy nie doszła, a wykonanie wznowiło się od pierwszej instrukcji za pętlą.

break: zatrzymaj się, gdy odpowiedź jest znana

Typowe użycie break to wyszukiwanie: gdy już znajdziesz to, czego szukasz, nie ma powodu przeglądać reszty.

Wynik:

first invalid email at index 1

Iterator (i++) nie uruchamia się po break, więc i nadal zawierałoby indeks znalezionego elementu, gdyby było zadeklarowane poza pętlą. Przy prostych wyszukiwaniach to samo mówi wywołanie z biblioteki: Array.FindIndex(emails, e => !e.Contains("@")) zwraca indeks (lub -1), a emails.Any(e => !e.Contains("@")) z LINQ odpowiada, czy błędny adres w ogóle istnieje.

continue: przejdź do następnej iteracji

continue to klauzula strażnika dla pętli. Zamiast owijać właściwą pracę w if, odrzuć niechciane przypadki na samym początku:

Wynik:

pen costs 1.20
mug costs 8.00

To, dokąd continue przechodzi dalej, zależy od pętli. W pętli for uruchamia iterator, a potem sprawdza warunek. W pętli while skacze prosto do warunku, co prowadzi do klasycznego błędu:

int i = 0;
while (i < 10)
{
    if (i % 2 == 0)
        continue;        // i++ below is skipped: i stays 0 forever
    Console.WriteLine(i);
    i++;
}

Aktualizuj licznik przed każdym continue albo użyj pętli for, w której aktualizacja jest w nagłówku i zawsze się wykonuje.

break wewnątrz switch wewnątrz pętli

W switch instrukcja break kończy sekcję przypadku. Gdy ten switch jest w pętli, break należy do switcha, a nie do pętli:

Wynik:

quit received
count = 2

Zwykłe break w przypadku "quit" zakończyłoby tylko switch, pętla działałaby dalej, a count wyniosłoby 3. goto skacze do etykiety za pętlą. Przeniesienie pętli do metody i napisanie return w tym przypadku też działa i nie wymaga etykiety.

Wychodzenie z pętli zagnieżdżonych

C# nie ma break z etykietą, jak break outer; w Javie. Gdy wyszukiwanie przechodzi przez dwie zagnieżdżone pętle, break w pętli wewnętrznej kończy tylko ją; pętla zewnętrzna przechodzi do następnego wiersza. Są trzy standardowe rozwiązania.

1. Flaga, którą sprawdza pętla zewnętrzna:

Wynik:

first free seat: row 1, seat 2

2. goto do etykiety za obiema pętlami. To jedyne miejsce, w którym wiele przewodników stylu C# akceptuje goto, bo wyraża dokładnie "wyjdź z obu pętli":

for (int row = 0; row < rows; row++)
{
    for (int col = 0; col < cols; col++)
    {
        if (seats[row, col] == 0)
        {
            Console.WriteLine($"first free seat: row {row}, seat {col}");
            goto done;
        }
    }
}
done:
Console.WriteLine("search finished");

3. Metoda z return, zwykle najczystsza, bo wyszukiwanie dostaje nazwę i może zwrócić wynik:

Wynik:

first free seat: row 1, seat 2

C# 7.0 wprowadził też funkcje lokalne, które pozwalają umieścić taką metodę pomocniczą wewnątrz metody wywołującej. Użyty tu wzorzec z parametrem out jest wyjaśniony na stronie o ref i out.

goto i etykiety

Etykieta to identyfikator zakończony dwukropkiem, a goto label; do niej skacze. Reguły są na tyle ścisłe, że zapobiegają najgorszym nadużyciom:

  • Etykieta musi być w tej samej metodzie, w tym samym bloku co goto albo w bloku go otaczającym. Z bloków można wyskakiwać na zewnątrz, ale nigdy do środka: skok spoza pętli do etykiety w jej ciele to błąd CS0159 (no such label within the scope of the goto statement).
  • Z bloku finally nie można wyjść przez goto, break ani continue.
  • Wewnątrz switch instrukcje goto case 3; i goto default; przekazują sterowanie do innej sekcji. W ten sposób switch w C# wyraża przechodzenie między przypadkami, bo niejawne wpadnięcie do następnego przypadku to błąd kompilacji.

goto może też budować pętle (skok wstecz do etykiety), ale pętla while mówi to samo w formie, którą rozpozna każdy czytelnik. Zostaw goto do wychodzenia z pętli zagnieżdżonych i do sekcji switch.

return, throw i pętle

return opuszcza całą metodę, więc opuszcza też każdą pętlę w jej wnętrzu. throw robi to samo, zgłaszając wyjątek. Wewnątrz bloku try z finally wszystkie instrukcje break, continue, goto i return nadal wykonują blok finally, zanim sterowanie pójdzie dalej, dzięki czemu można bezpiecznie wyjść z pętli, która trzyma otwarty plik w bloku using.

InstrukcjaOpuszczaKontynuuje w
continuebieżącą iteracjęnastępnej iteracji (iterator, potem warunek)
breaknajbardziej wewnętrzną pętlę lub switchinstrukcji za nią
goto labeldowolne otaczające blokietykiecie
returnmetodękodzie wywołującym
throwwszystko aż do pasującego catchbloku catch

Najczęściej zadawane pytania

Czym różni się break od continue w C#?

break natychmiast kończy najbardziej wewnętrzną pętlę (lub switch); wykonanie przechodzi za nią. continue kończy tylko bieżącą iterację i przeskakuje do następnej: w pętli for najpierw uruchamia się iterator, w while i do while najpierw sprawdzany jest warunek, a w foreach pobierany jest następny element.

Jak wyjść z pętli zagnieżdżonych w C#?

C# nie ma break z etykietą. Są trzy możliwości: ustawić flagę bool i sprawdzać ją w pętli zewnętrznej, przeskoczyć za obie pętle przez goto do etykiety albo przenieść pętle do metody i wyjść z niej przez return. Wersja z metodą jest zwykle najczystsza, bo daje wyszukiwaniu nazwę i wartość zwracaną.

Czy break wewnątrz switch wychodzi z pętli w C#?

Nie. Wewnątrz switch umieszczonego w pętli break kończy sekcję switch, a pętla działa dalej. Żeby wyjść z pętli z wnętrza przypadku, użyj return, flagi sprawdzanej za switchem albo goto do etykiety za pętlą. continue wewnątrz przypadku przechodzi do następnej iteracji pętli.

Czy goto jest dozwolone w C#?

Tak. goto label; skacze do label: w tej samej metodzie, ale nie może wskoczyć do bloku ani wyjść z finally. Wewnątrz switch instrukcje goto case value; i goto default; przeskakują między sekcjami. Poza wychodzeniem z pętli zagnieżdżonych i jawnym przechodzeniem między przypadkami switch goto rzadko jest najczytelniejszym wyborem.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ