2016-03-02 17 views

risposta

3

Si può provare a utilizzare return come questo:

foreach(//do some stuff) 
    foreach(//do some stuff) 
     if(//check some condition) 
      return; 
+1

questo è quello che mi serve –

+3

Anche se questo può servire agli scopi dell'OP, 'return' fa più che uscire dai loop - esce dalla funzione che contiene i loop! E in tutta onestà agli altri suggerendo le risposte, da nessuna parte OP ricorda che questi loop fanno parte di una funzione che restituisce il valore che stavano per ottenere. – rskar

4

risposta rapida:

foreach(//do some stuff) 
{ 
    foreach(//do some stuff) 
    { 
      if(//check some condition) 
      { 
       goto end; // I'd probably add a comment here 
      } 
    } 
    // *1 
} 
end: 
{} // the rest of your code. 

ma, ma ... SESE

Le violazioni SESE sono violazioni del principio Single Entry Single Exit. E 'abbastanza facile da risolvere, utilizzando una condizione in più:

bool found = false; 
for (int i=0; i<foo.Count && !found; ++i) 
{ 
    for (int j=0; j<bar.Count; ++j) 
    { 
     if (...) { found = true; } 
    } 
    // *1 
    if (!found) { ... } 
} 

Allora perché usare un GOTO qui?

Credo che creare un codice corretto e gestibile significhi utilizzare i costrutti del linguaggio che descrivono meglio il proprio intento. "Codice" qui è sempre composto da due cose:

  • flusso di controllo, che si esprime attraverso le cose come for, while, break e goto.
  • Flusso di dati, che viene espresso tramite espressioni, variabili e altro accesso alla memoria.

L'intento dell'OP è di uscire da un ciclo annidato, che equivale a un'operazione di controllo del flusso. Pertanto, ritengo che dovresti utilizzare l'operazione del flusso di controllo che rappresenta più da vicino quale sia l'intento, che in questo caso è uno goto.

Si noti che questo non è affatto un motivo per cui si dovrebbe abusare per l'introduzione di dichiarazioni goto dappertutto; se lo fai, il codice diventerà molto difficile da leggere, il che non ha nulla a che fare con la manutenibilità e la leggibilità. È necessario considerare l'istruzione goto come un'istruzione di "controllo di ultima istanza", che viene utilizzata molto raramente nel codice correttamente predisposto.

Detto questo, in questo caso, ciò significa che non è necessario creare variabili locali per gestire il flusso di controllo, a meno che ciò sia assolutamente necessario (ad esempio se non sono disponibili costrutti linguistici in grado di esprimere chiaramente il proprio intento). Per lo stesso motivo, non avrei usato Linq in questo particolare scenario.

Voglio prestazioni. Cosa dovrei fare?

Credo che la maggior parte dell'abuso di costrutti linguistici deriva dal non capire come il compilatore si occupa del codice, motivo per cui prendo l'abitudine di spiegare parti di come funziona internamente. Tieni presente che ti consiglio di utilizzare un numero goto perché descrive più chiaramente il tuo intento, non perché potrebbe essere un po 'più veloce. Ecco:

Immagina di essere il compilatore. Hai un sacco di codice al punto di * 1 nel tuo codice e non puoi usare return. Ora ci sono due opzioni:

  1. È possibile utilizzare un goto.
  2. È possibile utilizzare la bandiera extra.

L'opzione 1 si compila in un campo, che ha memoria. La memoria è "scarsa" nel senso che i compilatori faranno del loro meglio per consumare meno memoria possibile, preferibile nei registri. Ecco da dove proviene la tua performance. Quindi, il compilatore tenterà di eliminare la bandiera.

Per fare questo, il compilatore eseguirà una tonnellata di analisi del flusso e altre cose, nel tentativo di determinare che in realtà ci sono due percorsi di codice: uno è quando la bandiera è impostata e l'altra se è non.

Ora, se sei fortunato, il compilatore avrà il suo momento "aha" e cambierà il tuo codice da (2) a un semplice GOTO, nel qual caso il cielo è ancora blu e tutti sono felici.

Tuttavia, se non si è fortunati (e ci sono un sacco di motivi pratici perché ciò accada), non lo rileverà dall'analisi del flusso e non creerà il GOTO. Dato che il tuo flag è utilizzato nel ciclo interno, potrebbe anche allocare un registro per questo, che potrebbe essere lo scenario peggiore.

Se si fosse utilizzato lo goto in primo luogo, non è necessario tutto questo. Semplicemente dai al compilatore la soluzione giusta. Semplice.

Hrm avete ulteriori dettagli su come il compilatore fa questo?

Sì, un'occhiata a questo video due ore di Chandler che spiega molto su come funzionano i compilatori: https://www.youtube.com/watch?v=FnGCDLhaxKU

-updated- A quanto pare alcune persone misintepreted mia storia come è stato sottolineato da @Groo. Ho apportato alcune modifiche per chiarire cosa intendevo dire.

+4

https://xkcd.com/292/ – Loofer

+1

@Loofer: una volta in una luna blu, ti lasci usare 'goto', soprattutto se non puoi mettere' return'. –

+0

@Loofer I loop sono compilati anche su IL GOTO e LABEL. Basta non prendere l'abitudine se hai un'alternativa. – atlaste

1
bool doBreak = false; 
foreach(//do some stuff) 
{ 
    foreach(//do some stuff) 
    { 
     doBreak = <check some condition>; 
     if(doBreak) break; 
    } 
    if(doBreak) break; 
} 
4

Si potrebbe farlo utilizzando una 'bandiera'

bool breakout = false 
foreach(//do some stuff) 
{ 
    foreach(//do some stuff) 
    { 
      if(//check some condition) 
      { 
      breakout = true; 
      break; 
      } 
    } 
    if(breakout) 
     break; 
} 
1

È possibile utilizzare MENTRE. Forse questo codice consente a voi

foreach(// Some condition here) 
{ 
    //solution 
    bool breakme = false; 

    while (// Some condition here) 
    { 
     foreach (// Some condition here) 
     { 
      if (// Condition again) 
      { 
       //Do some code 
      } 
      if (// Condition again) 
      { 
       //Stop the first foreach then go back to first foreach 
       breakme = true; 
       break; 
      } 
     } 
    } 
    if(breakme) 
    { 
     break; 
    } 
} 
2

Se non è possibile return, io suggerisco di usare Linq, rende il codice leggibile :

Boolean found = false; 

foreach(var item1 in source1.TakeWhile(_ => !found)) { 
    foreach(var item2 in source2.TakeWhile(_ => !found)) { 
      if (some condition) 
      { 
       found = true; 

       //break; // Not necessary 
      } 
    } 
} 
+0

"Leggibile" è negli occhi di chi guarda, immagino. :) – Groo