‘Goto’ è così male?

Dopo aver fatto qualche ricerca su come sfondare un ciclo secondario

while (true) { // Main Loop for (int I = 0; I < 15; I++) { // Secondary loop // Do Something break; // Break main loop? } } 

la maggior parte delle persone consiglia di chiamare la funzione ‘goto
Guardando come il seguente esempio:

 while (true) { // Main Loop for (int I = 0; I < 15; I++) { // Secondary Loop // Do Something goto ContinueOn; // Breaks the main loop } } ContinueOn: 

Però; Ho spesso sentito dire che la frase “goto” è una ctriggers pratica. L’immagine qui sotto illustra perfettamente il mio punto: Serie trovata

Così

  • Quanto è pessima la dichiarazione goto e perché?
  • Esiste un modo più efficace per interrompere il ciclo principale rispetto all’utilizzo dell’istruzione “goto”?

MODIFICARE:

Quanto è pessima la dichiarazione goto e perché?

Dipende dalla situazione esatta. Non riesco a ricordare in nessun momento dove ho trovato reso il codice più leggibile del refactoring. Dipende anche dalla tua personale visione della leggibilità – alcune persone non la amano più di altre, come risulta dalle altre risposte. (Come punto di interesse, è ampiamente usato nel codice generato – tutto il codice asincrono / attesa in C # 5 si basa su molti goto).

Il problema è che le situazioni in cui tende ad essere utilizzato tendono ad essere il tipo di situazioni in cui il refactoring aiuta comunque le cose – mentre goto attacca con una soluzione che diventa più difficile da seguire in quanto il codice diventa più complicato.

Esiste un modo più efficace per interrompere il ciclo principale rispetto all’utilizzo dell’istruzione “goto”?

Assolutamente. Estrai il tuo metodo in una funzione separata:

 while (ProcessValues(...)) { // Body left deliberately empty } ... private bool ProcessValues() { for (int i = 0; i < 15; i++) { // Do something return false; } return true; } 

Generalmente preferisco fare questo introducendo una variabile locale extra per tenere traccia di "ho finito" - anche se questo funzionerà, ovviamente.

Quanto è pessima la dichiarazione goto e perché?

È davvero male per tutte le normali ragioni fornite. È perfetto quando si emulano loop etichettati in lingue che non li supportano.

Sostituendolo con le funzioni, in molti casi si genererà una logica scatter che dovrebbe essere letta come la stessa unità. Questo rende più difficile leggere. A nessuno piace seguire una serie di funzioni che in realtà non fanno nulla fino alla fine del viaggio, quando hai in qualche modo dimenticato da dove hai iniziato.

Sostituirlo con booleani e una serie di if e interruzioni aggiuntivi è davvero molto complicato e rende più difficile seguire le reali intenzioni, come qualsiasi rumore.

In java (e javascript), questo è perfettamente accettabile (loop etichettati):

 outer: while( true ) { for( int i = 0; i < 15; ++i ) { break outer; } } 

In C #, sembra che l'equivalente molto vicino non lo sia:

 while( true ) { for (int I = 0; I < 15; I++) { goto outer; } } outer:; 

A causa della parola goto , che ha un effetto psicologico nel far sì che le persone abbandonino tutto il loro buon senso e le facciano linkare xkcd indipendentemente dal contesto.

Esiste un modo più efficace per interrompere il ciclo principale rispetto all'utilizzo dell'istruzione "goto"?

In alcuni casi non c'è, ed è per questo che le altre lingue forniscono loop etichettati e C # fornisce goto . Tieni presente che il tuo esempio è troppo semplice e fa sì che il work-around non sembri troppo negativo perché sono fatti su misura per l'esempio. In effetti, potrei benissimo suggerire questo:

  for (int I = 0; I < 15; I++) { break; } 

Cosa ne pensi di questo:

 int len = 256; int val = 65536; for (int i = 0; i < len; i++) { for (int j = 0; j < len; j++) { if (i + j >= 2 * val) { goto outer; } val = val / 2; } } outer:; 

Ti sembra ancora bene?

 int len = 256; int val = 65536; for (int i = 0; i < len; i++) { if (!Inner(i, ref val, len)) { break; } } private bool Inner(int i, ref int val, int len) { for (int j = 0; j < len; j++) { if (i + j >= 2 * val) { return false; } val = val / 2; } return true; } 

Ho intenzione di non essere d’accordo con tutte le altre risposte qui. Il codice che presenti usando goto non ha nulla di sbagliato in questo. C’è una ragione per cui C # ha un’istruzione goto , ed è proprio per questi tipi di scenari che descrivi.

goto ha semplicemente uno stigma negativo perché negli anni ’70 e in quelli precedenti la gente scriveva un codice orribile, completamente non mantenibile, in cui il stream di controllo era saltato dappertutto a causa del goto . Il goto C # non consente nemmeno la transizione tra i metodi! Eppure c’è ancora questo stigma irrazionale contro di esso.

A mio parere, non c’è assolutamente nulla di sbagliato nell’usare un goto “moderno” per uscire da un loop interno. Le persone “alternative” offerte finiscono sempre per essere più complicate e più difficili da leggere .

In genere si suppone che i metodi siano riutilizzabili . Fare un intero metodo separato per la parte interna di un ciclo, che verrà sempre chiamato da quella posizione, e dove l’implementazione del metodo potrebbe finire in una posizione distante nel codice sorgente, non è un miglioramento.

Questo tipo di esercizio sciocco per evitare goto è fondamentalmente l’equivalente della correttezza politica, ma nel mondo della programmazione.

Sono d’accordo con la maggior parte delle risposte su quanto sia negativo goto.

La mia supposizione per evitare goto sta facendo qualcosa del genere:

 while (condition) { // Main Loop for (int i = 0; i < 15; i++) { // Secondary loop // Do Something if(someOtherCondition){ condition = false // stops the main loop break; // breaks inner loop } } } 

A volte uso “goto” e ho trovato che sembra buono, come nell’esempio sopra;

 bool AskRetry(Exception ex) { return MessageBox.Show(you know here...) == DialogResult.Retry; } void SomeFuncOrEventInGui() { re:try{ SomeThing(); } catch (Exception ex) { if (AskRetry(ex)) goto re; else Mange(ex); // or throw or log.., whatever... } } 

So che puoi fare la stessa cosa in modo ricorsivo, ma a chi importa si lavora e io uso.

Un mio collega (che ha 15 anni in programmazione firmware) e io uso sempre goto. Tuttavia, lo usiamo solo per gestire le eccezioni !! Per esempio:

 if (setsockopt(sd,...)<0){ goto setsockopt_failed; } ... setsockopt_failed: close(sd); 

Per quanto ne so, questo è il modo migliore per gestire le eccezioni in C. Credo, anche io lavoro per C #. Inoltre, non sono l'unico a pensarla così: esempi di buona gotos in C o C ++

Vai a sono pericolosi oltre agli altri problemi di menzione. La compilazione corretta di bypass di Goto in alcune lingue prende C # per esempio nonostante non abbia dichiarazioni di ritorno in questa funzione, questo verrà compilato.

 public int Test() { goto one; one: { goto one; } } 

Personalmente mi piace pensare a goto come “goto conduce all’inferno” … e per molti aspetti questo è vero in quanto può portare a codice altamente non mantenibile e pratiche veramente cattive.

Detto questo, è ancora implementato per una ragione e, se usato, dovrebbe essere usato con parsimonia, se non è disponibile un’altra soluzione. Le lingue OO si prestano a non averne realmente bisogno (molto).

Le uniche volte in cui l’ho sempre visto in questi giorni sono in offuscamento … un esempio di utilizzo di goto per creare codice dall’inferno, così le persone saranno scoraggiate dal cercare di capirlo!

Alcune lingue dipendono da parole chiave equivalenti. Ad esempio in x86 assember ci sono parole chiave come JMP (Jump), JE (Jump if Equal) e JZ (Jump if Zero). Questi sono richiesti frequentemente in linguaggio assembly in quanto non esiste OO a questo livello e ci sono pochi altri metodi per spostarsi all’interno di un’applicazione.

AFAIK … stai lontano da esso a meno che ASSOLUTAMENTE NECESSARIO.