2011-05-05 38 views
11

Tengo un bucle ForEach que procesa una lista bastante grande de contactos. En lugar de hacer un montón de procesamiento en este ciclo principal, llamo a un método que a su vez llama a un método, etc. Los métodos llaman a otros métodos en otras clases, tal vez en otros espacios de nombres.¿Cómo 'continuo' un bucle ForEach desde un método anidado?

¿Cómo rompo el método actual si se detecta una condición y deseo pasar al siguiente contacto? Estoy en un método de algunos niveles del bucle ForEach principal.

Normalmente, puede usar la palabra clave continue dentro de ForEach para pasar al siguiente elemento de la colección. continue no es una opción en este caso. Cuando escribo continue, se subraya en rojo con un comentario de "Mensaje no resuelto".

Entonces, ¿qué debo hacer?

+0

¿Puede proporcionar un código de muestra? –

Respuesta

3

tal vez podría tener una bandera ... algo así como bool conditionDetected y cuando se detecta una condición que acaba de establecer en true y el tener if (conditionDetected) return; de los métodos hasta llegar a la cima donde if(conditionDetected) continue a la siguiente .. . Luego lo configura de nuevo en falso y continúa ... obtiene un error porque no está dentro del bucle foreach cuando se mueve a otro método

1

Una solución sería lanzar una excepción personalizada que burbujearía y finalmente quedaría atrapada en el circuito foreach.

Asegúrese de estar utilizando una excepción personalizada (es decir, tener su propio tipo y no solo usar una declaración catch(Exception)), de esta manera usted sabe que definitivamente está capturando la correcta.

En el bloque catch, simplemente continúe con su bucle foreach (o manipúlelo apropiadamente).

try { 
    MethodWithIteration(i); 
} catch (ProcessingFailedException) { 
    continue; 
} 

Si está fallando por una razón específica, usted es mejor nombrar la excepción apropiada, de esta manera usted no tiene un tipo de excepción sólo para controlar el flujo de su aplicación, y de hecho es significativo. En el futuro, es posible que desee manejar esto.


foreach(DoSomethingWithMe doSomething in objList) 
{ 
    // Option 1 : Custom Processing Exception 
    try 
    { 
     ProcessWithException(doSomething); 
    } catch(ProcessingFailedException) 
    { 
     // Handle appropriately and continue 
     // .. do something .. 
     continue; 
    } 

    // Option 2 : Check return value of processing 
    if (!ProcessWithBool(doSomething)) 
     continue; 

    // Option 3 : Simply continue on like nothing happened 
    // This only works if your function is the only one called (may not work with deeply-nested methods) 
    ProcessWithReturn(doSomething); 
} 
+15

Ewwww ... excepciones para el control de flujo es asqueroso –

+0

He añadido un semi-descargo de responsabilidad ;-) –

6

Sus métodos de apoyo probablemente debería devolver un valor que se comprueba en el bucle para el método principal, y luego continue a partir de ahí, si eso es lo que el valor indica.

El uso de excepciones es otra alternativa, pero en general se consideran más lentas. No estoy seguro acerca de C# en particular. También se consideran generalmente de mala forma cuando se usan de esta manera. Se deben lanzar excepciones en situaciones excepcionales, no como una parte normal del control de flujo. Podría decirse que hay situaciones en las que está bien usarlas de esta manera, como el marco web Play !, pero probablemente no estés en ninguna de ellas.

+1

es el control de flujo estilo Monad ;-) – BrokenGlass

0

para continuar necesita estar directamente dentro del circuito, por lo que Necesita estar al tanto de la ruptura en el ciclo, pero ¿no podría simplemente devolver un bool de los métodos?

int[] ints = {1,2,3,4,5,6}; 

foreach (var k in ints) 
{ 
    if (Continue(k)) 
    { 
     continue; 
    } 
} 


bool Continue(int k) 
{ 
    return what's suitable 
} 
2

No hay una manera simple de lograr esto. El único lugar donde puede forzar la próxima iteración del bucle foreach es directamente desde su cuerpo. Por lo tanto, para hacerlo debe salir del método y volver al cuerpo del bucle foreach.

Hay un par de maneras de lograr este

  • lanzar una excepción: Por favor, por favor no hacer esto. Las excepciones no deben utilizarse como un mecanismo de control de flujo
  • Salir del método, y todos los métodos entre usted y el foreach bucle con un código de retorno que hace que el cuerpo para ejecutar una sentencia continue
2

Si estoy la comprensión de su problema correctamente, usted tiene un bucle de este modo:

for(int i = 0; i < 100; i++) 
{ 
    DoComplexProcessing(); 
} 

DoComplexProcessing llama luego otro método, que llama a otro método y así sucesivamente.

Una vez que esté abajo, digamos, 4 niveles, detecta una condición (lo que sea) y desea cancelar esa iteración de su DoComplexProcessing.

Suponiendo que sea correcto, lo que yo haría es tener un objeto que acompaña a la cadena de métodos como un parámetro out En cada nivel, una vez que se encuentra la condición "incorrecta", devolvería nulo (u otro valor predeterminado) cuando null no es una opción) y establece el objeto de referencia en un estado que significa 'abortar'. Cada método verificaría entonces el estado 'abortar' y luego haría lo mismo con "return null, set object to 'abort'" call.

Algo como esto:

TracerObject tracer = new tracer("good"); 
for(int i = 0; i < 100; i++) 
{ 
    DoComplexProcessing(out tracer) 
    if(tracer.status == "abort") DoSomethingElse() 
} 

el siguiente método podría hacer esto

DoComplexProcessing(out TracerObject tracer) 
{ 
    var myObject = new MyObject() 
    myObject.Property = DoSlightlyLessComplexProcessing(myObject, out tracer) 
    if(tracer.Status == "abort") 
    { 
    //set myObject.Property to some default value 
    } 
    return myObject; 
} 
} 
20

Vas por mal camino aquí; da un paso atrás y reconsidera tu diseño.

En general, es una muy mala idea tener métodos que intenten influir en el flujo de control de las personas que llaman. Un método es el servidor de la persona que llama, no el maestro. El método no decide qué hace la persona que llama después; ese no es asunto suyo Por el contrario, un método:

  • realiza un cálculo y devuelve un resultado, o
  • modifica algún estado, y luego
  • lanza una excepción si la operación no se pudo completar con éxito

Hay son estilos de flujo de control avanzado en los que las calles trabajan junto con las personas que llaman para determinar "qué pasa después" - Estilo de paso de continuación, por ejemplo. Pero no deberías ir allí. Ellos son muy difíciles de entender.

1

Para extender la respuesta de Erics, la solución es refactorizar sus bucles para que el bucle externo tenga más control e influencia sobre los métodos de larga ejecución que llama.

Por ejemplo supongamos que tiene botones que permiten al usuario o bien omitir un contrato o cancelar el procesamiento del todo "saltar" y "cancelar" - se podía strucutre su código un poco como esto:

foreach (var contact in contacts) 
{ 
    if (Cancel) 
    { 
     return; 
    } 
    ContactProcessor processor = new ContactProcessor(contact); 
    processor.Process(contact); 
} 

class ContactProcessor 
{ 
    public bool Skip { get; set; } 

    private readonly Contact contact; 
    public ContactProcessor(Contact contact) 
    { 
     this.contact = contact; 
    } 

    public void Process() 
    { 
     if (!this.Skip) 
     { 
      FooSomething(); 
     } 
     if (!this.Skip) 
     { 
      BarSomething(); 
     } 
     // Etc... 
    } 

    publiv void BarSomething() 
    { 
     // Stuff goes here 
     if (this.contact.WTF()) 
     { 
      this.Skip = true; 
     } 
    } 
} 

(Hay es obvio que hay mucho de carne para hacer aquí)

La idea es que si el control del procesamiento es importante para usted, entonces los métodos y las clases responsables de ese procesamiento deben tener mecanismos de control "horneados". Tener a toda una clase a cargo del procesamiento, si a menudo es una buena manera de encapsular esto, también hace que sea muy fácil hacer cosas como reportar el progreso.

Lo anterior permite que cualquier método del ContactProcessor detecte si debe omitir el procesamiento (sin excepciones) y configure el indicador Skip. También permite potencialmente que el bucle externo establezca el indicador Skip (por ejemplo, según la entrada del usuario).

Cuestiones relacionadas