# Etykietowane break i continue w C# 15: kiedy i po co używać nowych etykiet pętli
W C# 15 pojawiła się długo oczekiwana, lecz kontrowersyjna funkcja — etykietowane break i continue. Mimo krytyki ze strony części społeczności, ta nowość rozwiązuje konkretne problemy związane z pracą z zagnieżdżonymi pętlami i wyrażeniami switch. Jest to szczególnie istotne w sytuacjach, gdy chcemy wyjść nie z najbliższego bloku, lecz z zewnętrznej pętli bez użycia goto czy flag logicznych.
Problem wyjścia z zagnieżdżonych konstrukcji
Tradycyjnie w C# brakowało możliwości jawnego wskazania, z której dokładnie pętli należy wyjść przy użyciu operatorów break lub continue. Prowadziło to do konieczności wprowadzania dodatkowych flag logicznych, rozbijania kodu na metody lub używania goto — co przeczy zasadom czystego kodu. Na przykład przy przetwarzaniu dwuwymiarowej struktury danych często trzeba natychmiast przerwać wszystkie iteracje po spełnieniu określonego warunku:
string foundValue = null;
bool shouldBreak = false;
for (int x = 0; x < xMax; x++)
{
for (int y = 0; y < yMax; y++)
{
foundValue = GetValue(x, y);
if (foundValue == targetValue)
{
shouldBreak = true;
break;
}
}
if (shouldBreak)
{
break;
}
}
ProcessValue(foundValue);
Ten sposób nie tylko zaśmieca kod, ale też stwarza ryzyko błędów — na przykład wywołanie ProcessValue nawet jeśli wartość nie została znaleziona. Alternatywa w postaci wyodrębnienia logiki do osobnej metody poprawia czytelność, ale nie zawsze jest możliwa, zwłaszcza gdy stan pętli zależy od kilku zmiennych zewnętrznych.
Rzeczywiste scenariusze użycia etykietowanego break/continue
Najbardziej przekonującym przypadkiem użycia etykiet jest praca z switch wewnątrz pętli. Bez etykietowanego break nie da się wyjść z samej pętli z wnętrza gałęzi case. Wcześniej programiści musieli sięgać po goto lub owijać logikę w lambdy/lokalne funkcje. Teraz rozwiązanie jest proste:
outer: foreach (var item in collection)
{
switch (item.Type)
{
case ItemType.StopAll:
break outer;
case ItemType.SkipGroup:
continue outer;
}
}
Taki kod jednoznacznie wyraża intencję: break outer kończy całą pętlę, a continue outer przechodzi do następnej iteracji zewnętrznej pętli. Jest to szczególnie przydatne w parserach, automatach skończonych i systemach routingu zdarzeń.
Kiedy unikać etykiet
Mimo technicznej poprawności etykietowanego break i continue nie należy ich używać na potęgę. Ich zastosowanie jest uzasadnione tylko w sytuacjach, gdzie alternatywy pogarszają czytelność lub wydajność. W większości przypadków lepsze są:
- Wyodrębnienie logiki do osobnych metod z wczesnym zwrotem (early return).
- Użycie operatorów LINQ (FirstOrDefault, Any itp.) do wyszukiwania wartości.
- Stosowanie flag tylko przy prostej strukturze zagnieżdżeń.
Etykiety są uzasadnione, gdy:
- Pętla zawiera switch z wieloma case wymagającymi sterowania przepływem na poziomie zewnętrznej pętli.
- Wydajność jest kluczowa, a wyodrębnienie do metody niepożądane z powodu kosztów.
- Logika nie nadaje się do dekompozycji bez utraty spójności semantycznej.
Porównanie podejść: etykiety vs refaktoryzacja
Rozważmy trzy sposoby realizacji tego samego scenariusza — wyszukiwanie wartości w macierzy z możliwością pomijania wierszy i przedwczesnego zakończenia:
Sposób 1: Flagi
— Plusy: kompatybilność ze wszystkimi wersjami C#.
— Minusy: wysoka złożoność poznawcza, ryzyko błędów.
Sposób 2: Podział na metody
— Plusy: czytelność, testowalność, zgodność z zasadami SOLID.
— Minusy: możliwe zwiększenie głębokości stosu wywołań.
Sposób 3: Etykietowane break/continue
— Plusy: kompaktowość, precyzyjne wyrażenie intencji.
— Minusy: ograniczona stosowalność, nietypowość dla części programistów.
Wybór zależy od kontekstu. Dla logiki biznesowej najlepszy jest sposób 2. Dla kodu systemowego lub parserów — sposób 3.
Co ważne
- Etykietowane break i continue w C# 15 rozwiązują wąski, ale realny problem: sterowanie przepływem z zagnieżdżonych switch lub pętli.
- Nie używaj etykiet zamiast refaktoryzacji: jeśli logika jest złożona, lepiej wyodrębnić ją do osobnej metody.
- Klasyczny przykład użycia — przetwarzanie kolekcji przez foreach z switch, gdzie niektóre case mają kończyć lub pomijać iteracje zewnętrznej pętli.
- Funkcja jest w statusie Open, ale już przyjęta do Working Set grupy językowej C#.
- Unikaj porównywania nieekwiwalentnych przykładów: stary i nowy kod muszą rozwiązywać tę samą task.
— Editorial Team
Brak komentarzy.