fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
3 minut

Jedyny przypadek gdy GOTO nie jest FUJ


22.08.2008

Jedna z zasad, której młodzi programiści uczą się na początku swojej kariery (żeby nie powiedzieć “wysysają z mlekiem swojego nauczyciela”) brzmi:

“instrukcja GOTO w językach programowania poziomu wyższego niż asembler istnieje po to i tylko po to, aby świadomie ignorować jej egzystencję”

Prawda? I co tu dużo gadać, ciężko się z tą teorią nie zgodzić. Jedyne do czego prowadzi używanie tej instrukcji to powstanie tzw “unmaintable spaghetti code”.

Chyba że…
Jest moim zdaniem jeden scenariusz, w którym instrukcja GOTO czasami się w C# przydaje. Chodzi mianowicie o “switch fallthrough” – spójrzmy na następujący kod:

1:   switch (number)
2:   {
3:   	case 0:
4:   		DoSomething();
5:   	case 1:
6:   		DoSomethingElse();
7:   		break;
8:   	default:
9:   		throw new Exception();
10:   }

W Javie, w C, w C++ i w milionie innych języków taka kontrukcja spowoduje wykonanie obu metod dla number==0 oraz jednej metody dla number==1. A co się stanie w C#? W C# dostaniemy w twarz błędem kompilacji: “Control cannot fall through from one case label (‘case 0:’) to another” (wtrącenie: coś mi się kojarzy, że w C# 1.0 taka konstrukcja była poprawna, jednak nie mam zainstalowanego VS 2003 żeby to sprawdzić i nie dam sobie niczego uciąć). Dobrze to czy źle… nie mnie teraz oceniać. Zainteresowanych odsyłam do uzasadnienia w dziale Visual C# Developer Center. Idźmy jednak dalej: aby osiągnąć takie samo zachowanie w C# musimy jawnie je opisać w ten sposób:

1:   switch (number)
2:   {
3:   	case 0:
4:   		DoSomething();
5:   		goto case 1;
6:   	case 1:
7:   		DoSomethingElse();
8:   		break;
9:   	default:
10:   		throw new Exception();
11:   }

Inaczej po prostu się nie da, i jest to moim zdaniem jedyny (a 1>0!) uzasadniony scenariusz użycia konstrukcji GOTO w C#.

Na koniec jeszcze kilka słów, coby nie powstało niezamierzone “misandersztendiś”: pomiędzy liniami tego posta nie ma stwierdzenia “switch z goto jest dobry”. Instrukcja switch sama w sobie trochę zalatuje makaronem (na szczęście da się z tym coś zrobić). Jeśli jednak i tak mamy w kodzie switcha to równie dobrze możemy weń wstawić goto, lepsze to niż wielokrotne pisanie tych samych instrukcji dla różnych case’ów (ileż amerykanizmów i anglizmów w jednym zdaniu! aż poczułem hamburgera w ustach i krople deszczu na czole…).


UWAGA! Stosować z rozwagą! Głupota i bezmyślność może dość szybko skończyć się wiecznym gniciem w programistycznej “hall of shame”, czyli The Daily WTF. I żeby potem nie było na mnie;).

Nie przegap kolejnych postów!

Dołącz do ponad 9000 programistów w devstyle newsletter!

Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit
Notify of
dario-g

Można opakować switcha i zrobić też tak:
0: void GotoSwitch(int number){
1:   switch (number)
2:   {
3:   case 0:
4:   DoSomething();GotoSwitch(1);
5:   break;
6:   case 1:
7:   DoSomethingElse();
8:   break;
9:   default:
10:   throw new Exception();
11:   }
12: }
;))

arkadiusz.wasniewski
arkadiusz.wasniewski

Jak dla mnie powyższe uzasadnienie jest mało przekonujące. Patrząc na kod mogę stwierdzić, iż wymaga on refaktoryzacji. Metoda DoSometing() powinna wywoływać wewnętrznie DoSometingElse().
Pozdrawiam
Arek

Uno
Uno

goto przede wszystkim uzywa sie do wyjscia z zagniezdzonych petli. Poza tym twoj kod duzo lepiej zapisac prostolinijnie:
1:   switch (number)
2:   {
3:   case 0:
4:   DoSomething();
7:   DoSomethingElse();
5:   break;
6:   case 1:
7:   DoSomethingElse();
8:   break;
9:   default:
10:   throw new Exception();
11:   }
niz stosowac “uzasadnione uzycie goto”

Procent

@arek:
Oczywiście, że ten kod wymaga refaktoryzacji. Pewnie stąd w treści posta znalazł się link do strony http://www.refactoring.com :) Dodatkowo – warunkowe wywołanie DoSomethingElse() niekoniecznie musi być dobrym pomysłem.
@Uno:
Goto używa się w wielu sytuacjach, w tym także do “wyjścia z zagnieżdżonych pętli”. Publikując tą notkę chciałem ujawnić moje stanowisko w tej sprawie – uważam że nie powinno się stosować goto do wyjścia z zagnieżdżonych pętli, od tego mamy ‘break’.
Co do przedstawionego “zapisu prostolinijnego”, o tym też napisałem: “lepsze to [goto] niż wielokrotne pisanie tych samych instrukcji dla różnych case’ów”.

Wojciech Gebczyk
Wojciech Gebczyk

Przypomnialo mi sie ze ostatnio (wczoraj) widzialem w kodzie C (dokladniej kodzie C ktory chcial uchodzic za C++ – czyli mial klasy i…. tyle :P). Byla tam 17 stronnicowa metoda (po wydrukowaniu z VS) gdzie dosc duzo bylo goto. Uzyte to bylo do zakonczenia przetwarzania jakis obliczen gdy uzytkownik anulowal operacje. Skok bylo do miejsca “sprzatania”. Mozna bylo to latwo uproscic doadjac metode z wlasciym kodem operacji gdzie zamiwst goto bylby return a zwenetrzna funkcja by po zwracanym wyniku wiedziala czy sprztaca czy robic cos innego. To wlasciwy komentarz. 1. Nie zawsze break zadziala jesli mamy petle w petli i… Read more »

Gregi
Gregi

Jak sie wychodzi z zagniezdzonych petli uzywajac (tylko) break?
Uwazam, ze goto powinno sie stosowac, bo inaczej mamy potworki w stylu funkcji GotoSwitch, albo znanej konstrukcji do{ } while(0);
Zdecydowanie goto sie przydaje – moze do jego uzycia trzeba po prostu dojrzec? ;-)

arkadiusz.wasniewski
arkadiusz.wasniewski

Programuję 10 lat i nigdy nie zdarzyło mi się mieć potrzeby użycia goto. Jeśli kod jest dobrze napisany to nie ma szans na goto.

Gutek

Zgadzam sie za Arkiem.
Jezeli w ktoryms momencie dochodze do tego, ze jestm i potrzebne GoTo to zastanawiam sie gdzie poplenilem blad i poprawiam kod tak by GoTo nie bylo.
Gutek

Procent

@Gregi:
Nawet jeżeli nie tylko break, to na pewno nie powinno się tego robić za pomocą goto. A dojrzewanie… moim zdaniem to raczej w drugą stronę: ‘faktycznie, goto zmniejsza czytelność, utrudnia modyfikacje, i ogolnie sux’ ;).
@Arek, Gutek:
No to chyba… git. Mi zdarzyło się raz, właśnie w opisanym scenariuszu.

chojrak tchórzliwy pies
chojrak tchórzliwy pies

przejrzyjcie źródła glibc, jądra linuxa i innych ważnych projektów. aż roi się tam od goto i to nie dlatego, że pisały go lamy (choć takie jest pewno podejście większości). co prawda użycie goto często powoduje, że kod w C niewiele zaczyna różnić się od asemblera, ale daje programiście wolność w pełnym zakresie i niejednokrotnie pozwala pisać wydajniejszy kod. słusznie przytoczono wyjście z zagnieżdżonych pętli, czy wręcz – wyjście z wstawki asemblerowej do konkretnego miejsca we fragmencie C. goto, jak każda inna konstrukcja języka programowania, powinno być stosowane w sytuacjach, w których jest to właściwe. pal sześć czytelność jeśli można zyskać… Read more »

Moja książka

Facebook

Zobacz również