2010-11-10 7 views
7

¿Existe alguna forma mejor de escribir este código sin usar goto? Parece incómodo, pero no puedo pensar en una mejor manera. Necesito poder realizar un intento de reintento, pero no quiero duplicar ningún código.Mejor manera de escribir la lógica de reintento sin ir a

public void Write(string body) 
{ 
    bool retry = false; 
RetryPoint: 
    try 
    { 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
    } 
    catch (Exception) 
    { 
     if(retry) 
      throw; 
     // try to re-open the file... 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
     retry = true; 
     goto RetryPoint; 
    } 
} 
+10

lo siento, no puede resistir! http://xkcd.com/292/ – Joe

+2

SIEMPRE hay una mejor manera de escribir la lógica sin un goto. –

+1

@McWafflestix: No estoy de acuerdo. Hay algunos * casos muy raros * en los que el uso de 'goto' de hecho produce un código más limpio: la ruptura de los bucles anidados es un ejemplo comúnmente citado (ya que C# no tiene roturas etiquetadas como Java). Consulte http://stackoverflow.com/questions/2542289/is-there-ever-a-reason-to-use-goto-in-modern-net-code para obtener más información. – Heinzi

Respuesta

15

Aquí es la lógica básica que iba a utilizar en lugar de una instrucción goto:

bool succeeded = false; 
int tries = 2; 

do 
{ 
    try 
    { 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
     succeeded = true; 
    } 
    catch(Exception) 
    { 
     tries--; 
    } 
} 
while (!succeeded && tries > 0); 

acabo # agregado de la lógica intentos, a pesar de que la pregunta original no tenía ninguna.

+0

Esto realiza intentos infinitos, utiliza la sintaxis C++ para la captura y no se vuelve a lanzar. No tengo idea de por qué fue votado cuando está claramente equivocado. –

+0

Ah, y no vuelve a abrir en caso de falla. –

+2

@Steven Sudit: el cuerpo del bloque catch implica que es el mismo que el código original. – LBushkin

1

¿Qué pasa si lo pones en un bucle? Algo similar a esto, tal vez.

while(tryToOpenFile) 
{ 
    try 
    { 
     //some code 
    } 
    catch 
    { 
    } 
    finally 
    { 
     //set tryToOpenFile to false when you need to break 
    } 
} 
+0

Esto tiene infinitos intentos y generalmente no resuelve el problema. –

+0

Sí, por eso dije * algo similar *. La idea era hacerlo en un ciclo, que parece ser la idea general en cada respuesta aquí. – Joe

+0

Derecha, usar un ciclo es bastante obvio (aunque no universal). El diablo está en los detalles. –

0

intentar algo así como lo siguiente: solución

int tryCount = 0; 
bool succeeded = false; 

while(!succeeded && tryCount<2){ 
    tryCount++; 
    try{ 
     //interesting stuff here that may fail. 

     succeeded=true; 
    } catch { 
    } 
} 
+0

El bool no es necesario. Más al punto, esto no arroja la última falla. –

1
public void Write(string body, bool retryOnError) 
{ 
    try 
    { 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
    } 
    catch (Exception) 
    { 
     if(!retryOnError) 
      throw; 
     // try to re-open the file... 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
     Write(body, false); 
    } 
} 
+0

Recursividad? De Verdad? –

+0

@Steven: Claro, ¿por qué no? Es una repetición simple de la cola y muy legible: 'Write (body, false)' documenta claramente la intención del escritor (intente lo mismo otra vez pero no lo intente) y evita saturar el código (no loop, no 'succeeded' variable). Reemplazar 'retryOnError' con un conteo de reintentos también es un ejercicio fácil ... – Heinzi

+0

Algunas razones. Debido a la forma en que GC funciona, la recursividad mantiene los objetos vivos por más tiempo, por lo que debe evitarse cuando una solución iterativa es lo suficientemente clara. Cuando la optimización de la recursividad de la cola es posible, este es un problema menor. Este ejemplo no recurre más de una vez, pero cambiaría el bool a una cuenta regresiva si desea aumentar eso (y probablemente lo haría). Tal cambio magnificaría los efectos. –

5

de Michael no acaba de cumplir los requisitos, que son para volver a intentar un número fijo de veces, lanzando el último fracaso.

Para esto, recomendaría un simple bucle, contando hacia abajo. Si tiene éxito, salga con un descanso (o, si es conveniente, regrese). De lo contrario, deje que la captura revise si el índice está abajo a 0. Si es así, vuelva a lanzar en lugar de iniciar sesión o ignorar.

public void Write(string body, bool retryOnError) 
{ 
    for (int tries = MaxRetries; tries >= 0; tries--) 
    { 
     try 
     { 
      _outputfile.Write(body); 
      _outputfile.Flush(); 
      break; 
     } 
     catch (Exception) 
     { 
      if (tries == 0) 
       throw; 

      _outputfile.Close(); 
      _outputfile = new StreamWriter(_filepath, true); 
     } 
    } 
} 

En el ejemplo anterior, un retorno habría estado bien, pero quería mostrar el caso general.

+1

'if (tries == 0)' –

+0

@Anthony: Gracias, eso fue un error. Lo arreglaré de inmediato. –

+0

Acabo de hacer un cambio más que creo que se aplica a cualquier solución: lo hice 'Cerrar' el viejo editor antes de abrir uno nuevo. Si esto no se hace, el antiguo mantendrá un asa abierta hasta que el GC entre, bloqueando el funcionamiento de los nuevos. –

4

@Michael's answer (con un bloque de captura correctamente implementado) es probablemente el más fácil de usar en su caso, y es el más simple. Sin embargo, en el interés de presentar alternativas, aquí es una versión de que factores del control de flujo "reintentar" en un método separado:

// define a flow control method that performs an action, with an optional retry 
public static void WithRetry(Action action, Action recovery) 
{ 
    try { 
     action(); 
    } 
    catch (Exception) { 
     recovery(); 
     action(); 
    } 
} 

public void Send(string body) 
{ 
    WithRetry(() => 
    // action logic: 
    { 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
    }, 
    // retry logic: 
    () => 
    { 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
    }); 
} 

Se podría, por supuesto, mejorar esto con cosas como el recuento de reintento, una mejor propagación de errores, y así.

+0

No estoy seguro de qué hacer con esto. Podría decirse que este tipo de solución impulsada por delegados es elegante, flexible y reutilizable. Por otra parte, actualmente no hace realmente lo que se necesita. Al final, no votaré ni por arriba ni por abajo. –

+0

@Steven: ¿Extrañé algo? ¿De qué manera no cumple los requisitos? – LBushkin

+0

Las formas que mencionaste: no tiene un conteo de reintentos, no se lanza en el último intento, etc. Sin duda, puedes * hacer * lo que sea necesario - He visto suficientes de tus respuestas para estar seguro de eso - pero no lo has hecho en este momento. –

0

con un valor lógico

public void Write(string body) 
{ 
     bool NotFailedOnce = true;    
     while (true) 
     { 
      try 
      { 
       _outputfile.Write(body); 
       _outputfile.Flush();   
       return;      
      } 
      catch (Exception) 
      { 
       NotFailedOnce = !NotFailedOnce; 
       if (NotFailedOnce) 
       { 
        throw; 
       } 
       else 
       { 
        m_Outputfile = new StreamWriter(m_Filepath, true); 
       } 
      } 
     }   
} 
+0

Esto no puede funcionar. Por un lado, 'NotFailedOnce =! NotFailedOnce)' siempre será falso, por lo que nunca se lanzará. En cambio, se repetirá por siempre. –

+0

@Steve, mira el código nuevamente, no es una verificación de igualdad, sino una asignación. – Jimmy

+0

eso es inteligente, es decir, malo. El compilador generará advertencias para eso, y debería hacerlo, ya que una asignación en medio de un predicado se debe probablemente a un error tipográfico. Del mismo modo, es inteligente/malo establecer un bool en falso al NOTARLO en lugar de, ya sabes, establecerlo en falso. Este código es innecesariamente ofuscado. –

Cuestiones relacionadas