Qualcuno usa ancora [goto] in C # e, in caso affermativo, perché? [chiuso]


104

Mi chiedevo se qualcuno utilizza ancora la sintassi della parola chiave "goto" in C # e quali possibili ragioni ci sono per farlo.

Tendo a vedere tutte le affermazioni che inducono il lettore a saltare il codice come una cattiva pratica, ma mi chiedevo se esistessero scenari credibili per l'utilizzo di tale sintassi.

Vai alla definizione delle parole chiave


3
cosa intendi con "fermo"? C'è stato un periodo di tempo in cui le persone lo hanno usato tutto il tempo [in c #]?
Massif

4
@Massif: "still" intendeva enfatizzare l'opinione moderna sull'uso di "goto" come preludio al codice spaghetti e alla mancanza di leggibilità nel codice sorgente. Molto raramente vedi esempi di codice che includano questa particolare parola chiave, motivo per cui ero interessato a chiedere in primo luogo.
Brian Scott

50
Se il lettore "salta in giro" il codice è una cattiva pratica, allora eviti anche "interrompere", "continuare", "lanciare" e "tornare"? Tutti causano un ramo nel flusso di controllo, a volte un ramo non locale. "Throw" non ti dice nemmeno dove sta andando, a differenza di goto.
Eric Lippert

2
Uso gotoper interrompere un ciclo e tornare alla dichiarazione di partenza in base alla condizione specifica
Nitin Sawant

3
Mi piace goto. Ho provato ad evitarlo a causa della tendenza delle persone che dicono di evitarlo perché rende il codice più difficile da leggere. Avendo imparato il linguaggio Assembly e le istruzioni di ramo, penso che a volte ci siano momenti, potrebbe rendere il codice più leggibile. Penso che più usi in un metodo e saltare troppo in basso nel codice possano fare più male che bene. Ma se stai pensando che un goto funzionerebbe bene qui, un semplice goto una volta ogni tanto non dovrebbe farti fare di tutto per evitare solo perché il consenso comune è evitarlo.
eaglei22

Risposte:


93

Ci sono alcuni (rari) casi in cui goto può effettivamente migliorare la leggibilità. In effetti, la documentazione a cui ti sei collegato elenca due esempi:

Un uso comune di goto consiste nel trasferire il controllo a una specifica etichetta switch-case o all'etichetta predefinita in un'istruzione switch.

L'istruzione goto è utile anche per uscire da loop profondamente annidati.

Ecco un esempio per quest'ultimo:

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

end_of_loop:

Naturalmente, ci sono anche altri modi per aggirare questo problema, come il refactoring del codice in una funzione, l'utilizzo di un blocco fittizio attorno ad esso, ecc. (Vedere questa domanda per i dettagli). Come nota a margine, i progettisti del linguaggio Java hanno deciso di vietare completamente goto e introdurre invece un'istruzione break etichettata .


48
Normalmente proverei a rifattorizzare questo per mettere i loop in un metodo separato da cui potrei semplicemente tornare da ...
Jon Skeet

2
@ Heinzi - Non ho visto goto garantito. Come dice Jon, se viene "garantito", il codice chiede di essere refactoring.
manojlds

29
etichettato break, solo un modo più lungo di dire goto perché fa la stessa cosa assurda ...
Jesus Ramos

20
@ Jesus ma poi con goto, puoi andare ovunque. Interruzione etichettata assicura che stai andando appena fuori dal ciclo.
mihsathe

1
A meno che tu non sia avventuroso e usi goto con un indirizzo (l'ho visto fare prima), il problema è mitigato. E dubito che qualcuno stia usando deviazioni nel codice C # e Java per sfruttare le istruzioni goto.
Jesus Ramos

66

Ricordo questa parte

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; 
}

A qualcosa di simile

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

Fare riferimento Questa


17
In realtà vedo questo come il motivo più valido per utilizzare [goto] ancora. Almeno in questo scenario aumenta la leggibilità per i programmatori inconsapevoli che i casi si sovrappongono senza un'istruzione break.
Brian Scott

11
@Brian Scott, V4Vendetta. A meno che non mi sbagli, la prima istruzione non viene compilata in C #. Ciò aiuterebbe la comprensione del programmatore.
Jodrell

2
V4Vendetta perché hai inserito un'interruzione nel primo frammento di codice ...? Era meglio mostrarlo senza interruzione, altrimenti i due frammenti fanno cose diverse. Il motivo per cui hai bisogno del goto nel secondo esempio è precisamente perché il primo non si compila in C # (come farebbe in C).
Stephen Holt

23

Lo uso ampiamente in Eduasync per mostrare il tipo di codice che il compilatore genera per te quando usi metodi asincroni in C # 5. Vedresti la stessa cosa nei blocchi iteratori.

Nel codice "normale" però, non ricordo l'ultima volta che l'ho usato ...


1
puoi fornire un piccolo esempio del motivo per cui questo approccio è stato preferito o era semplicemente una preferenza personale?
Brian Scott

1
@ Brian: non è proprio chiaro cosa intendi. Eduasync mostra il codice C # equivalente a quello che fa il compilatore per te - e genera codice che usa goto, in modo efficace ...
Jon Skeet

9

goto è ottimo per rompere molti loop in cui break non funzionerebbe bene (ad esempio in condizioni di errore) e, come ha detto Kragen, goto è usato dal compilatore per generare istruzioni switch e anche altre cose.


7
Sicuramente "break" / "continue" sono approcci migliori alla gestione del loop piuttosto che richiedere all'editor del codice di saltare il codice sorgente cercando di capire dove si verifica il passaggio successivo?
Brian Scott

6
Non se hai loop annidati.
Jesus Ramos

1
ok, lo vedo come uno scenario valido.
Brian Scott

1
In una condizione di errore, dovresti considerare di lanciare un'eccezione.
Jodrell

6
Se vuoi gestire l'errore internamente senza eccezioni, questo sarebbe un modo valido per farlo.
Jesus Ramos

8

Non ricordo di aver mai usato goto. Ma forse migliora l'intento di un ciclo per sempre che non vuoi davvero mai uscire (no break, ma puoi ancora returno throw):

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

Poi di nuovo, un semplice while (true)dovrebbe essere sufficiente ...

Inoltre, si potrebbe utilizzare in una situazione in cui si desidera che la prima iterazione di un ciclo per iniziare a metà del ciclo: cerca qui per un esempio.


La risposta collegata contiene un uso "interessante" di goto.. e while(true) {..}non è un uso interessante ..
user2864740

5

Il compilatore utilizza le gotoistruzioni in varie parti del codice generato, ad esempio nei tipi di blocchi iteratori generati (generati quando si utilizza la yield returnparola chiave: sono abbastanza sicuro che i tipi di serializzazione XML generati abbiano anche alcune gotoistruzioni da qualche parte.

Vedere i dettagli sull'implementazione del blocco Iterator: macchine a stati generati automaticamente per ulteriori dettagli sul perché / come il compilatore C # gestisce questo.

Oltre al codice generato, non c'è una buona ragione per utilizzare gotoun'istruzione nel codice normale: rende il codice più difficile da capire e di conseguenza più soggetto a errori. D'altra parte usandogoto istruzioni nel codice generato come questo può semplificare il processo di generazione e normalmente va bene perché nessuno leggerà (o modificherà) il codice generato e non c'è possibilità che vengano commessi errori perché una macchina sta facendo la scrittura.

Vedere la dichiarazione Go-to considerata dannosa per un argomento contro gotononché un classico pezzo di storia della programmazione.


4
Questo è stupido: ci sono diversi casi in cui goto è utile, come illustrato da altre risposte. O li strofini esplicitamente, o semplicemente dicendo "è sbagliato" è, beh, sbagliato.
o0 '.

@ Lohoris non lo sto comprando - ogni esempio che ho visto in cui goto "migliora la leggibilità" (comprese le risposte qui) sarebbe molto più leggibile dopo un semplice refactoring.
Justin

3
@ Justin no, a volte i cicli annidati sono solo il modo più naturale di fare qualcosa, ad esempio se stai attraversando un array di array.
o0 '.

6
@ Justin non è sempre più chiaro averlo in una funzione. Stai forzando qualcosa (avendo una funzione) solo per evitare qualcosa che odi religiosamente (usando un goto). Chiara indicazione di fare qualcosa di sbagliato.
o0 '.

3
Le chiamate a @Justin Functions hanno un sovraccarico. gotonon. Qualcosa da considerare.
Dan Bechard

2

Il processore implementa almeno un'istruzione di salto e sono sicuro che molte istruzioni usano quelle nella loro implementazione o interpretazione.

Uno degli aspetti positivi dell'utilizzo di un linguaggio di terza o quarta generazione è che questi dettagli fisici vengono astratti da noi. Sebbene dovremmo essere consapevoli della legge dell'astrazione leaky, penso che dovremmo anche usare i nostri strumenti come sono previsti ( scusa ). Se stessi scrivendo codice e mi gotosembrasse una buona idea, sarebbe il momento di eseguire il refactoring. Lo scopo di un linguaggio strutturato è evitare questi "salti" e creare un flusso logico nella nostra ingegneria.

Dovrei evitare l'uso di, breakma non posso trascurare il vantaggio in termini di prestazioni. Tuttavia, se ho loop annidati che ne hanno bisogno reciprocamente break, è tempo di refactoring.

Se qualcuno può proporre un uso gotoche sembra migliore del refactoring, ritirare volentieri la mia risposta.

Spero di non essere colpevole di essere corso al " deposito delle biciclette " qui. Come dice Kragen, ciò che è abbastanza buono per Dijkstra è abbastanza buono per me.


1
Prendendo un dynamicoggetto e camminando è un oggetto grafico che contiene più dizionari per arrivare ai valori di cui ho bisogno. Non ha senso usare metodi che hanno un parametro di dynamicma si aspettano una forma esatta dell'oggetto. Con goto per rompere più livelli e continuare a camminare attraverso una raccolta di questi oggetti. [Non possiedo i tipi, quindi non posso fornire un accesso migliore, quindi riflesso o dinamico è]
Chris Marisic

-6

Goto non è mai migliore. E continuare, interrompere (eccetto in caso di cambio / caso), ritorno (multiplo) e lancio dovrebbero essere mantenuti al minimo. Non vuoi mai scappare dal centro dei loop del nido. Vuoi sempre che le istruzioni di controllo del ciclo abbiano tutto il controllo del ciclo. Il rientro contiene informazioni e tutte queste affermazioni gettano via quelle informazioni. Potresti anche eliminare tutti i rientri.


10
Vorresti interrompere un ciclo se non ha senso mantenere l'esecuzione del ciclo. Altrimenti finirai per sprecare più tempo di elaborazione in cicli più lunghi senza motivo.
Skuld

12
@ Kirk, questo suona come un'opinione piuttosto che come qualcosa di quantitativo?
Brian Scott
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.