Czy ktoś nadal używa [goto] w C #, a jeśli tak, to dlaczego? [Zamknięte]

104

Zastanawiałem się, czy ktoś nadal używa składni słowa kluczowego „goto” w C # i jakie są tego możliwe powody.

Zwykle postrzegam wszelkie stwierdzenia, które powodują, że czytelnik przeskakuje po kodzie, jako złą praktykę, ale zastanawiałem się, czy istnieją jakieś wiarygodne scenariusze użycia takiej składni?

Przejdź do definicji słowa kluczowego

Brian Scott
źródło
3
co masz na myśli mówiąc „nadal”? Czy był taki okres, w którym ludzie używali go cały czas [w języku c #]?
Masyw
4
@Massif: „still” miało na celu podkreślenie współczesnej opinii o używaniu „goto” jako wstępu do kodu spaghetti i braku czytelności w kodzie źródłowym. Bardzo rzadko można zobaczyć przykłady kodu zawierające to konkretne słowo kluczowe, dlatego w pierwszej kolejności byłem zainteresowany zapytaniem.
Brian Scott
50
Jeśli „skakanie” po kodzie przez czytelnika jest złą praktyką, to czy unikasz również „łamania”, „kontynuowania”, „rzucania” i „powrotu”? Wszystkie one powodują gałąź w przepływie sterowania, czasami gałąź nielokalną. „Throw” nawet nie mówi ci, dokąd zmierza, w przeciwieństwie do goto.
Eric Lippert
2
Używam gotodo przerwania pętli i powrotu do instrukcji początkowej zgodnie z określonym warunkiem
Nitin Sawant
3
Lubię goto. Próbowałem tego uniknąć, ponieważ ludzie mówili, aby go unikać, ponieważ utrudnia to odczytanie kodu. Po nauczeniu się języka asemblera i instrukcji gałęzi, myślę, że czasami zdarza się, że kod może być bardziej czytelny. Myślę, że wiele zastosowań w jednej metodzie i przeskakiwanie zbyt głęboko w kodzie może wyrządzić więcej szkody niż pożytku. Ale jeśli myślisz, że polecenie goto dobrze tu zadziała, proste polecenie goto od czasu do czasu nie powinno sprawić, że będziesz musiał zejść z drogi, aby tego uniknąć tylko dlatego, że powszechnym konsensusem jest unikanie go.
eaglei 22

Odpowiedzi:

93

Istnieją (rzadkie) przypadki, w których goto może faktycznie poprawić czytelność. W rzeczywistości dokumentacja, do której utworzyłeś łącze, zawiera dwa przykłady:

Typowym zastosowaniem goto jest przekazanie kontroli do określonej etykiety przypadku przełącznika lub etykiety domyślnej w instrukcji przełącznika.

Instrukcja goto jest również przydatna do wyjścia z głęboko zagnieżdżonych pętli.

Oto przykład tego ostatniego:

for (...) {
    for (...) {
        ...
        if (something)
            goto end_of_loop;
    }
}

end_of_loop:

Oczywiście istnieją również inne sposoby obejścia tego problemu, takie jak refaktoryzacja kodu na funkcję, użycie dookoła niego pustego bloku itp. (Szczegóły w tym pytaniu ). Na marginesie, projektanci języka Java zdecydowali się całkowicie zakazać goto i zamiast tego wprowadzić oznaczoną przerwę .

Heinzi
źródło
48
Zwykle spróbuję to zmienić, aby umieścić pętle w oddzielnej metodzie, z której mógłbym po prostu wrócić z ...
Jon Skeet.
2
@Heinzi - nie widziałem, aby uzyskać gwarancję. Jak mówi Jon, jeśli jest to „uzasadnione”, kod błaga o refaktoryzację.
manojlds
29
oznaczona jako przerwa, po prostu dłuższy sposób na powiedzenie goto, ponieważ robi to samo dziwaczne działanie ....
Jesus Ramos
20
@Jesus, ale z goto możesz iść wszędzie. Oznaczona przerwa zapewnia wyjście tuż poza pętlę.
mihsathe
1
Jeśli nie jesteś żądny przygód i nie używasz goto z adresem (widziałem to wcześniej), problem ten zostanie złagodzony. I wątpię, czy ktoś używa objazdów w kodzie C # i Java do wykorzystywania instrukcji goto.
Jesus Ramos
66

Pamiętam tę część

switch (a)     
{ 
    case 3: 
        b = 7;
        // We want to drop through into case 4, but C# doesn't let us
    case 4: 
        c = 3;
        break; 
    default: 
        b = 2;
        c = 4;
        break; 
}

Na coś takiego

switch (a)     
{
    case 3: 
        b = 7;
        goto case 4;    
    case 4: 
        c = 3;
        break;     
    default: 
        b = 2;
        c = 4;
        break;
}

Odnieś się do tego

V4Vendetta
źródło
17
Właściwie to uważam, że jest to najważniejszy powód, aby używać [goto]. Przynajmniej w tym scenariuszu zwiększa czytelność dla programistów nieświadomych, że przypadki wpadają w siebie bez instrukcji break.
Brian Scott
11
@Brian Scott, V4Vendetta. O ile się nie mylę, pierwsza instrukcja nie kompiluje się w C #. Pomogłoby to programistom w zrozumieniu.
Jodrell
2
V4Vendetta dlaczego wstawiłeś przerwę w pierwszym fragmencie kodu ...? Lepiej było pokazać to bez przerwy, w przeciwnym razie oba fragmenty robią różne rzeczy. Powodem, dla którego potrzebujesz goto w drugim przykładzie, jest właśnie to, że pierwszy nie kompiluje się w C # (tak jak w C).
Stephen Holt
23

Używam go szeroko w Eduasync, aby pokazać rodzaj kodu, który kompilator generuje dla Ciebie podczas używania metod asynchronicznych w C # 5. To samo można zobaczyć w blokach iteratorów.

Jednak w „normalnym” kodzie nie pamiętam, kiedy ostatnio go używałem ...

Jon Skeet
źródło
1
czy możesz podać mały przykład, dlaczego takie podejście było preferowane, czy też było to po prostu osobiste preferencje?
Brian Scott
1
@Brian: Nie jest jasne, co masz na myśli. Eduasync pokazuje kod C # odpowiadający temu, co kompilator robi za Ciebie - i generuje kod, który skutecznie używa goto ...
Jon Skeet
9

Goto świetnie nadaje się do przerywania wielu pętli, w których break nie działałby dobrze (powiedzmy, gdy wystąpił błąd), a jak powiedział Kragen, goto jest używany przez kompilator do generowania instrukcji przełączania i innych rzeczy.

Jesus Ramos
źródło
7
Z pewnością „przerwa” / „kontynuacja” są lepszym podejściem do zarządzania pętlą niż wymaganie od edytora kodu przeskakiwania po kodzie źródłowym, próbując zrozumieć, gdzie następuje następny krok?
Brian Scott
6
Nie, jeśli masz zagnieżdżone pętle.
Jesus Ramos
1
ok, widzę to jako prawidłowy scenariusz.
Brian Scott
1
W przypadku błędu należy rozważyć zgłoszenie wyjątku.
Jodrell
6
Jeśli chcesz obsłużyć błąd wewnętrznie bez wyjątku, byłby to prawidłowy sposób.
Jesus Ramos
8

Nie pamiętam, żebym kiedykolwiek używał goto. Ale może poprawia intencję wiecznej pętli, z której naprawdę nigdy nie chcesz wyjść (nie break, ale nadal możesz returnlub throw):

forever: {
  // ...
  goto forever;
}

Z drugiej strony while (true)wystarczy prosty ...

Ponadto użytkownik mógłby użyć w sytuacji, gdy chcesz pierwszej iteracji pętli rozpocznie się w środku pętli: Spójrz tutaj dla przykładu.

Jordão
źródło
Związane odpowiedź zawiera „ciekawe” wykorzystanie goto.. i while(true) {..}nie jest interesującym stosowanie ..
user2864740
5

Kompilator używa gotoinstrukcji w różnych fragmentach generowanego kodu, na przykład w generowanych typach bloków iteratorów (generowanych przy użyciu yield returnsłowa kluczowego - jestem prawie pewien, że wygenerowane typy serializacji XML również mają gotogdzieś kilka instrukcji.

Zobacz szczegóły implementacji bloku iteratora: automatycznie generowane maszyny stanu, aby uzyskać więcej informacji na temat tego, dlaczego / jak kompilator C # obsługuje to.

Poza wygenerowanym kodem nie ma dobrego powodu, aby używać gotoinstrukcji w normalnym kodzie - sprawia, że ​​kod jest trudniejszy do zrozumienia, a w rezultacie bardziej podatny na błędy. Z drugiej strony, użycie gotoinstrukcji w wygenerowanym kodzie, takim jak ten, może uprościć proces generowania i zwykle jest w porządku, ponieważ nikt nie będzie czytać (ani modyfikować) wygenerowanego kodu i nie ma szans na popełnienie błędów, ponieważ maszyna pisze.

Zobacz instrukcję Go-to uważaną za szkodliwą dla argumentu przeciwko, gotoa także klasyczny fragment historii programowania.

Justin
źródło
4
To głupie: istnieje kilka przypadków, w których funkcja goto jest przydatna, co ilustrują inne odpowiedzi. Albo je wyraźnie potrzesz, albo po prostu powiedzenie „to jest złe” jest, no cóż, złe.
o0 '.
@Lohoris Nie kupuję tego - każdy przykład, który widziałem, gdzie goto „poprawia czytelność” (w tym odpowiedzi tutaj) byłby znacznie bardziej czytelny po prostej refaktoryzacji.
Justin
3
@Justin nie, czasami zagnieżdżone pętle są po prostu najbardziej naturalnym sposobem zrobienia czegoś, na przykład podczas przechodzenia przez tablicę tablic.
o0' .
6
@Justin nie zawsze jest jaśniejsze, aby mieć to w funkcji. Wymuszasz coś (pełnisz funkcję) tylko po to, aby uniknąć czegoś, czego nienawidzisz religijnie (używając goto). Wyraźne wskazanie, że robisz coś złego.
o0 '.
3
Wywołania @Justin Functions mają narzut. gotonie. Coś do rozważenia.
Dan Bechard
2

Procesor implementuje co najmniej jedną instrukcję skoku i jestem pewien, że wiele instrukcji używa ich w swojej implementacji lub interpretacji.

Jedną z dobrych rzeczy w używaniu języka trzeciej lub czwartej generacji jest to, że te fizyczne szczegóły są od nas oderwane. Chociaż powinniśmy pamiętać o prawie nieszczelnej abstrakcji , myślę, że powinniśmy również używać naszych narzędzi zgodnie z ich przeznaczeniem ( przepraszam ). Gdybym pisał kod i gotowydawał się dobrym pomysłem, to byłby czas na refaktoryzację. Celem ustrukturyzowanego języka jest uniknięcie tych „skoków” i stworzenie logicznego przepływu w naszej inżynierii.

Powinienem unikać używania, breakale nie mogę przeoczyć korzyści związanych z wydajnością. Jeśli jednak mam zagnieżdżone pętle, które są wzajemnie potrzebne, breaknadszedł czas na refaktoryzację.

Jeśli ktoś może zaproponować zastosowanie tego goto, wydaje się lepsze niż refaktoryzacja, chętnie wycofam swoją odpowiedź.

Mam nadzieję, że nie jestem winny pośpiechu do „ szopy rowerowej ” tutaj. Jak mówi Kragen, to, co jest wystarczająco dobre dla Dijkstry, jest wystarczająco dobre dla mnie.

Jodrell
źródło
1
Biorąc dynamicobiekt i przechodząc po nim wykres obiektu, który zawiera wiele słowników, aby przejść do potrzebnych wartości. Nie ma sensu używać metod, które mają parametr, dynamicale oczekują dokładnego kształtu obiektu. Z goto, aby przełamać wiele warstw i kontynuować spacer po kolekcji tych obiektów. [Nie jestem właścicielem typów, więc nie mogę zapewnić lepszego dostępu, więc jest to refleksja lub dynamika]
Chris Marisic,
-6

Goto nigdy nie jest lepsze. I kontynuuj, łam (z wyjątkiem przełącznika / przypadku), (wielokrotny) powrót i rzut również powinny być ograniczone do minimum. Nigdy nie chcesz uciec ze środka pętli gniazd. Zawsze chcesz, aby instrukcje sterujące pętli miały całą kontrolę pętli. Wcięcie zawiera informacje, a wszystkie te stwierdzenia odrzucają te informacje. Równie dobrze możesz usunąć wszystkie wcięcia.

Kirk Augustin
źródło
10
Chciałbyś wyrwać się z pętli, jeśli nie ma sensu utrzymywanie jej wykonania. W przeciwnym razie stracisz więcej czasu przetwarzania w dłuższych pętlach bez powodu.
Skuld,
12
@Kirk, to brzmi raczej jak opinia niż cokolwiek ilościowego?
Brian Scott