2012-05-23 18 views
11

que he visto este patrón varias veces:Usando finalmente en lugar de captura

 bool success = false; 
     try 
     { 
      DoSomething(); 
      success = true; 
     } 
     finally 
     { 
      if (!success) 
       Rollback(); 
     } 

Y me he estado preguntando: ¿Por qué es mejor que el uso de captura para restauraciones?

 try 
     { 
      DoSomething(); 
     } 
     catch 
     { 
      Rollback(); 
      throw; 
     } 

¿Cuáles son las diferencias entre las dos formas de garantizar que los cambios se retrotraigan a la falla?

Respuesta

0

El objetivo claro aquí es hacer que se llame a Rollback en caso de error. Ambos fragmentos de código logran este objetivo. El primero usa un finally, que siempre se ejecuta, que verifica que se alcanzó con éxito la última línea del bloque try. El segundo detecta cualquier error, retrotrae la transacción y luego vuelve a lanzar la excepción que fue capturada. El resultado de cualquier fragmento es que cualquier excepción producida dará como resultado una reversión mientras sigue burbujeando hasta el siguiente nivel.

Mencionó que el proyecto se portó desde Java. En Java, puede puedere-throw an exception de forma similar a cómo puede hacerlo en C# utilizando throw;. También puede throw a new exception que aún mantendrá la pila de llamadas (et al). El segundo es un poco más claro/simple en C# (aunque no mucho) y el primero tiene la ventaja de funcionar en Java tal como está escrito.

+0

Esto tiene sentido. No me di cuenta de que Java no permite que vuelvas a lanzar excpetions correctamente. Y aquí estaba pensando que esto iba a tener algo que ver con secciones críticas y cosas como ... – configurator

+0

@configurator: Ahora que lo mencionas, creo que de hecho puede haber una conexión con [Regiones de Ejecución Restringida] (http://msdn.microsoft.com/en-us/library/ms228973.aspx). – dtb

+4

-1 para "En Java no hay forma de volver a lanzar una excepción" - [aparte de 'throw e', es decir] (http://stackoverflow.com/a/1097539/265143). –

1

finally siempre se ejecuta, no solo en la captura de excepciones.

Por supuesto, en este caso específico la reversión sólo es necesario cuando hubo un error, sino como un patrón general, la try-finally puede ser más útil para la gestión de recursos (donde a menudo es necesario asegurarse de que siempre Close() o Dispose() de su recurso correctamente). Especialmente si el autor del código proviene de Java, donde este idioma está más extendido.

+2

Pero cuando no hay excepción, 'success' será falso, por lo que la cláusula finally es no-operativa. – configurator

+0

@configurator, sí, mira mi actualización. –

+0

El comentario de fondo de Java tiene sentido dado el proyecto en el que he visto que esto realmente fue portado desde Java. – configurator

2

La declaración finally se usa generalmente para limpiar recursos. El método Rollback() podría estar bien usarlo allí si las excepciones no son las únicas razones para revertir la transacción. Close() o Dispose() métodos son los principales candidatos para terminar en el bloque finally.

Sin embargo, no desea ejecutar nada allí que pueda arrojar excepciones.

+4

Esta respuesta no tiene mucho que ver con la pregunta ... – configurator

+0

Es simplemente común hacer la limpieza de recursos en la declaración final, como mencioné. De esta manera no tienes que volver a tirar. –

+0

@configurator gracioso, es el más valorado hasta el momento, y hay otros similares, ¿tal vez edites la pregunta para obtener respuestas más acordes con tu deseo? –

4

Publiqué aquí algún código incluso si no está realmente relacionado con la pregunta (se eliminará más adelante).

Con este programa:

using System; 

namespace testcs 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       try 
       { 
        foo(); 
        foo(); 
        foo(); 
       } 
       catch 
       { 
        throw; 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.ToString()); 
      } 
     } 

     private static void foo() 
     { 
      throw new Exception("oops"); 
     } 
    } 
} 

El seguimiento de la pila (! Mirar a los números de línea) se conserva, pero dentro de la función main verá "línea 19", la línea donde throw es en cambio la verdadera línea donde se ha llamado al foo() (línea 13).

+3

+1, punto tomado, gracias por tomarse el tiempo para crear la muestra. No creo que necesariamente deba eliminarlo, ya que ilustra la diferencia entre los dos métodos en la pregunta (y definitivamente es demasiado largo para un comentario). – Heinzi

2

no estoy seguro si esto no es sólo anecdotal evidence, pero yo personalmente han utilizado este modelo para una razón muy práctica: Cuando DoSomething se produce una excepción, el depurador de Visual Studio se romperá en DoSomething donde se produce la excepción en el primer versión, mientras que se romperá en el throw; en la segunda versión. Esto permite inspeccionar el estado de la aplicación antes de que Rollback haya limpiado todo.

Screenshot

2

Si no le importa en este código particular qué tipo de excepción que es la captura de uso:

try 
{ 
    DoSomething(); 
    ok = true; 
} 
finally 
{ 
    if(!ok)Rollback(); 
} 

Esto conservará Pila de llamadas en su forma original para el 100%. Además, si utiliza mecanizado excepción de esta manera:

try 
{ 
    DoSomething(); 
    ok = true; 
} 
catch(FirstExcetionType e1) 
{ 
    //do something 
} 
catch(SecondExcetionType e2) 
{ 
    //do something 
} 
catch(Exception e3) 
{ 
    //do something 
} 
finally 
{ 
    if(!ok)Rollback(); 
} 

utilizando último, al final puede hacer que el código sea más fácil de leer que llamar a la reversión de todas las declaraciones de capturas individual.

Cuestiones relacionadas