for - next in c#
Salir de un bucle foreach desde dentro de un bloque de interruptores (12)
¿Cómo se sale de un bucle foreach mientras está dentro de un bloque de conmutación?
Normalmente, se usa break, pero si se usa un break dentro de un bloque de interruptores, simplemente saldrá de un bloque de conmutadores y el bucle foreach continuará su 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 está dentro del bloque del switch
es establecer un valor bool
colocado fuera del bucle en true y verificar el valor de este bool cada vez que se ingresa al foreach
y antes de ingresar al switch bloquear. 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 sigo pensando que debe haber una mejor manera de hacer esto que no conozco ...
EDIT: ¿Me pregunto por qué esto no se implementó en .NET de la manera realmente ordenada que funciona en PHP como lo menciona @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;
}
}
¿Honestamente? Esta es quizás la única situación en la que es completamente válido y apropiado 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
Algunos idiomas (sé que PHP es uno, no estoy seguro acerca de otros) le permiten especificar cuántas estructuras de control le gustaría romper con
descanso n;
donde 1 está implícito si simplemente rompes
break 2 haría lo que usted describe, si estuviera disponible en C #. No creo que ese sea el caso, por lo que su marca de salida es probablemente la mejor solución.
Basado en la documentación de MSDN en la declaración de 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 instrucció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 de enumerador.
El booleano es una forma. Otro es usar etiquetas y goto. Sé que la gente considera que goto es un pecado cardinal, pero que se usa con criterio (MUY con criterio), puede ser útil. En este caso, coloque una etiqueta justo después del final del bucle 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
EDITAR: algunas personas han mencionado sacar el bucle a un método separado para que pueda usar return. Veo el beneficio de esto ya que no requiere goto y también simplifica la función original que contenía 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 out o ref, es probable que 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 en lugar de más desordenado. Ponerlo en una función separada podría hacer que el código simple sea más difícil de leer.
Lame, lo sé, pero eso es todo lo que puedes hacer al respecto.
Siempre se puede transformar en un bucle while y agregar ''exitLoop'' como una condición que debe cumplirse. Dentro del conmutador, puede llamar para continuar y omitir el resto del pase actual y, como habría establecido exitLoop en falso, saldría como si fuera break. Aunque no es exactamente lo que estás preguntando, ¿quizás sea más elegante de esa manera?
No es realmente diferente de la exitLoop
, pero podría ser más legible si exitLoop
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;
}
Podría extraer su ciclo foreach al método separado y usar la declaración de return
. O podrías 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.
}
}
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 {}
Siempre existe la opción de reestructurar su código para que pueda return
de la instrucción switch
.
Su solución es prácticamente la opción más común en este caso. Dicho esto, pondría 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 extraer la instrucción switch y foreach loop en un método separado. Usted podría entonces simplemente return
desde dentro de la instrucción switch.
Transforme la instrucción switch () en una serie de instrucciones "if() else if() [...] else"
para que la break
salga del bucle foreach()
.
Usando goto
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
switch (number)
{
case 1:
break;
case 2:
goto breakLoop;
default:
break;
}
}
breakLoop:
Console.Write("Out of loop");