2010-10-28 26 views
46

¿Cómo se puede salir de un bucle foreach mientras se está dentro de un bloque de conmutadores?Salir de un bucle foreach desde dentro de un bloque de conmutadores

Normalmente, se utiliza descanso, pero si se utiliza un descanso dentro de un bloque de interruptores que sólo te va a salir de un bloque de interruptores y el bucle foreach continuará la ejecución:

foreach (var v in myCollection) 
{ 
    switch (v.id) 
    { 
     case 1: 
      if (true) 
      { 
       break; 
      } 
      break; 
     case 2; 
      break 
    } 
} 

Lo que estoy haciendo actualmente cuando necesito salir del foreach mientras estoy dentro del bloque switch estableciendo un valor bool colocado fuera del lazo en verdadero y comprobando el valor de este bool cada vez que se ingresa el foreach y antes de ingresar al bloque de interruptores. Algo como esto:

bool exitLoop; 
foreach (var v in myCollection) 
{ 
    if (exitLoop) break; 
    switch (v.id) 
    { 
     case 1: 
      if (true) 
      { 
       exitLoop = true; 
       break; 
      } 
      break; 
     case 2; 
      break 
    } 
} 

Esto funciona, pero no dejo de pensar que debe haber una mejor manera de hacer esto soy consciente de ...

EDIT: Me pregunto por qué esto no se implementó en .NET la realidad forma ordenada funciona en PHP según lo mencionado por @jon_darkstar?

$i = 0; 
while (++$i) { 
    switch ($i) { 
    case 5: 
     echo "At 5<br />\n"; 
     break 1; /* Exit only the switch. */ 
    case 10: 
     echo "At 10; quitting<br />\n"; 
     break 2; /* Exit the switch and the while. */ 
    default: 
     break; 
    } 
} 
+0

No estoy seguro de si existe una manera mejor, pero sería similar a tener que salir de un bucle externo mientras se está dentro del bucle interno. Hasta el momento, no he encontrado de todos modos para hacer eso y, por lo tanto, el uso de un valor de bandera parece ser la única manera. –

+0

Posible duplicado de [Salto de un ciclo while que contiene una instrucción switch] (http://stackoverflow.com/questions/1987379/break-out-of-a-while-loop-that-contains-a-switch- declaración) – ruffin

Respuesta

46

Su solución es prácticamente la opción más común en este caso. Dicho esto, me gustaría poner su cheque de salida al final:

bool exitLoop; 
foreach (var v in myCollection) 
{ 
    switch (v.id) 
    { 
     case 1: 
      if (true) 
      { 
       exitLoop = true; 
      } 
      break; 
     case 2; 
      break 
    } 

    // This saves an iteration of the foreach... 
    if (exitLoop) break; 
} 

La otra opción principal es refactorizar su código, y tire de la sentencia switch y el bucle foreach a cabo en un método separado. Entonces podría simplemente return desde el interior de la declaración del interruptor.

9

¿Honestamente? Esta es quizás la única situación en la que es totalmente válida y correcta de usar goto:

foreach (var v in myCollection) { 
    switch (v.id) { 
     case 1: 
      if (true) 
       // document why we're using goto 
       goto finished; 
      break; 
     case 2; 
      break 
    } 
} 
finished: // document why I'm here 
+2

¿Por qué usar goto? Es mucho mejor extraer el método. Cada vez que utilizas una declaración goto, un gatito muere. –

+3

¿Por qué extraer el método cuando puede simplemente ir? Son seis en uno, media docena el otro, realmente. Si el lazo es lo suficientemente pequeño como para pertenecer a una sola función en primer lugar, parece un poco extraño refactorizarlo únicamente para evitar un goto limpio y claro. – siride

+0

Porque usar goto causa dolor. Tus métodos deben ser lo más cortos posible. Y si lo son, nunca necesitarás una declaración goto. –

15

Usted puede extraer su ciclo foreach para el método separado y utilizar return comunicado. O podría hacer esto:

 foreach (object collectionElement in myCollection) 
     { 
      if (ProcessElementAndDetermineIfStop(collectionElement)) 
      { 
       break; 
      } 
     } 

     private bool ProcessElementAndDetermineIfStop(object collectionElement) 
     { 
      switch (v.id) 
      { 
       case 1: 
        return true; // break cycle. 
       case 2; 
        return false; // do not break cycle. 
      } 
     } 
+0

+1, esto es mucho más limpio –

1

Lame, lo sé, pero eso es todo lo que puede hacer al respecto.

Siempre puede transformarlo en un ciclo while y agregar 'exitLoop' como una condición que debe cumplirse. Dentro del interruptor, puede llamar para continuar salteando el resto del pase actual y como hubiera configurado exitLoop en falso, saldría de la misma manera que lo haría un salto. Aunque no es exactamente lo que estás preguntando, ¿quizás es más elegante de esa manera?

4

Siempre existe la opción de reestructurar su código para que pueda return de la declaración switch.

2

Basado en MSDN documentation en la declaración break, solo permite detener el alcance más alto.

Este caso es uno en el que podría usar una instrucción goto para dejar su bucle foreach. Si no desea utilizar una declaración goto, su solución parece ser la mejor.

Como nota al margen, podría mejorar su código probando el indicador exitLoop al final de la iteración, ahorrando el costo de una llamada al enumerador.

+0

Lo cual me parece bastante desafortunado, especialmente porque Java le permite salir de construcciones de bucle/conmutador multinivel con, por ejemplo, la sintaxis "break 2". – siride

20

El booleano es de una sola dirección. Otra es usar etiquetas y goto. Sé que la gente considera que ser un pecado cardinal, pero usado juiciosamente (MUY juiciosamente), puede ser útil. En este caso, coloque una etiqueta justo después del final del ciclo foreach. Cuando desee salir del bucle, simplemente vaya a esa etiqueta. Por ejemplo:

foreach(var v in myCollection) { 
    switch(v.Id) { 
     case 1: 
      if(true) { 
       goto end_foreach; 
      } 
      break; 
     case 2: 
      break; 
    } 
} 
end_foreach: 
// ... code after the loop 

EDIT: algunas personas han mencionado tomando el bucle a cabo en un método separado para que pueda utilizar el rendimiento. Veo el beneficio de esto ya que no requiere goto y también simplifica la función original que contiene el bucle. Sin embargo, si el bucle es simple y es el propósito principal de la función que lo contiene, o si el bucle hace uso de las variables de salida o de referencia, entonces probablemente sea mejor dejarlo en su lugar y usar el goto. De hecho, debido a que el goto y la etiqueta se destacan, probablemente hace que el código sea más claro que en lugar de clunkier. Ponerlo en una función separada podría dificultar la lectura del código simple.

+0

Exactamente. Bien dicho. – TomTom

+6

No usar goto. Hace que el código sea más complicado. El método de extracción es mucho mejor. –

+6

Será mejor si declara * why * goto hace que el código sea más complicado, especialmente cuando la alternativa es igual de complicada y aleja el código de su contexto original. – siride

6

en realidad no es diferente de su bandera exitLoop, pero podría ser más legible si se extrae un método ...

foreach (var v in myCollection) 
{ 
    if(!DoStuffAndContinue(v)) 
     break; 
} 


bool DoStuffAndContinue(MyType v) 
{ 
    switch (v.id) 
    { 
     case 1: 
      if (ShouldBreakOutOfLoop(v)) 
      { 
       return false; 
      } 
      break; 
     case 2; 
      break; 
    } 
    return true; 
} 
+0

Creo que esto responde mejor a la pregunta. –

1

Algunos idiomas (sé PHP es uno, no está seguro acerca de los demás) que permite para especificar de cuántas estructuras de control le gustaría salir con

break n;
donde 1 está implicado si solo rompe

break 2 haría lo que describe, si estuviera disponible en C#. No creo que ese sea el caso, por lo que su bandera de salida es probablemente la mejor solución.

+0

Me gusta mucho la forma en que esto se resolvió en PHP ... –

+0

sí, no soy demasiado para usar el break pero es bastante genial –

+0

PARI/GP también tiene esta característica 'break (n)'. –

-2

Transforme la instrucción switch() en una serie "if() else if() [...] else" de instrucciones para que break salga del circuito foreach().

+0

Siempre hay varias opciones para reemplazar un método por otro. Pero eso no responde la pregunta. La pregunta es: ¿cómo se puede salir de un bucle foreach mientras se está dentro de un bloque de interruptores? –

0

Puede hacerlo con Try/Catch .. Pero puede que no sea la mejor idea del mundo, ya que causa problemas de rendimiento y muestra líneas desagradables en la ventana de depuración.

try 
{ 
foreach (var v in myCollection) 
    { 
     switch (v.id) 
     { 
      case 1: 
       if (true) 
       { 
        throw new SystemException("Break"); 
       } 
       break; 
      case 2; 
       break; 
     } 
    } 
} catch {} 
+0

Las excepciones no son para control de flujo. Y si necesitan serlo, debería ser para un comportamiento algo excepcional, no algo tan normal como salir de un bucle. – siride

Cuestiones relacionadas