2008-09-24 16 views
167
personas

que he visto dicen que es mala forma de usar la captura sin argumentos, sobre todo si esa captura no hace nada:¿Por qué try {...} finalmente {...} es bueno; prueba {...} catch {} ¿malo?

StreamReader reader=new StreamReader("myfile.txt"); 
try 
{ 
    int i = 5/0; 
} 
catch // No args, so it will catch any exception 
{} 
reader.Close(); 

Sin embargo, esto se considera buena forma:

StreamReader reader=new StreamReader("myfile.txt"); 
try 
{ 
    int i = 5/0; 
} 
finally // Will execute despite any exception 
{ 
    reader.Close(); 
} 

Por lo que puedo decir, la única diferencia entre poner código de limpieza en un bloque finally y poner código de limpieza después de los bloques try..catch es si tiene instrucciones return en su bloque try (en ese caso, el código de limpieza finalmente se ejecutará, pero el código después de la try..catch no lo hará).

De lo contrario, ¿qué tiene de especial por fin?

+5

Antes de intentar atrapar un tigre que no puede manejar, debe documentar sus deseos finales. –

+0

[Excepciones] (http://stackoverflow.com/documentation/c%23/40/exception-handling#t=201609200630056810948) El tema de la documentación puede proporcionar algunas buenas ideas. También eche un vistazo al ejemplo [Finally Block] (http://stackoverflow.com/documentation/c%23/40/exception-handling/172/finally-block#t=201609200630056810948). – Athafoud

Respuesta

321

La gran diferencia es que try...catch se tragará la excepción, ocultando el hecho de que ocurrió un error. try..finally ejecutará su código de limpieza y luego la excepción continuará, para ser manejada por algo que sepa qué hacer con ella.

+10

Es probable que cualquier código escrito con encapsulamiento solo sea capaz de manejar la excepción en el punto donde se genera. Simplemente volviendo a subir la pila de llamadas en la desesperada esperanza de que algo más pueda manejar alguna excepción arbitral es una receta para el desastre. –

+2

Olvidaste de las excepciones sin marcar. Prácticamente cualquier método puede arrojar un OutOfMemoryError o algo similar. Desea que su bloque finalmente intente limpiar antes de salir. –

+3

En la mayoría de los casos, es más evidente por qué se produciría una excepción particular desde el nivel de aplicación (por ejemplo, una determinada configuración) que en el nivel de librray de clase. –

54

"Finalmente" es una afirmación de "Algo que siempre debe hacer para asegurarse de que el estado del programa sea correcto". Como tal, siempre es una buena forma tener uno, si hay alguna posibilidad de que las excepciones puedan descartar el estado del programa. El compilador también hace grandes esfuerzos para asegurarse de que se ejecute su código Finally.

"Capturar" es una afirmación de "Puedo recuperarme de esta excepción". Solo debes recuperarte de las excepciones que realmente puedes corregir; captura sin argumentos y dice "¡Oye, puedo recuperarme de cualquier cosa!", Que casi siempre es falso.

Si fuera posible recuperarse de cada excepción, entonces realmente sería una objeción semántica, acerca de lo que está declarando su intención de ser. Sin embargo, no lo es, y casi seguro que los marcos superiores a los tuyos estarán mejor equipados para manejar ciertas excepciones. Como tal, use finalmente, obtenga su código de limpieza ejecutándose de forma gratuita, pero deje que más manejadores expertos manejen el problema.

+1

Su opinión es generalizada, pero desafortunadamente ignora otro caso importante: invalidar expresamente un objeto cuyas invariantes ya no pueden contener. Un patrón común es que el código adquiera un bloqueo, realice algunos cambios en un objeto y libere el bloqueo. Si se produce una excepción después de realizar algunos, pero no todos, el objeto puede quedar en un estado no válido. Aunque en mi humilde opinión, las mejores alternativas * deberían * existir, no conozco mejor enfoque que detectar cualquier excepción que ocurra mientras el estado del objeto puede no ser válido, invalidar expresamente el estado y volver a lanzarlo. – supercat

2

Si no sabe qué tipo de excepción capturar o qué hacer con ella, no tiene sentido tener una declaración catch. Debería dejarlo solo para que una persona que llama más arriba tenga más información sobre la situación y sepa qué hacer.

Aún debe tener una instrucción finally allí en caso de que exista una excepción, para que pueda limpiar los recursos antes de que se envíe esa excepción a la persona que llama.

2

Desde una perspectiva de legibilidad, le dice más explícitamente a los futuros lectores de códigos que "esto es importante, debe hacerse sin importar lo que pase". Esto es bueno.

Además, las declaraciones de declaraciones vacías tienden a tener un cierto "olor" a ellas. Pueden ser una señal de que los desarrolladores no están pensando en las diversas excepciones que pueden ocurrir y cómo manejarlas.

1

Finalmente, puede limpiar los recursos, incluso si su instrucción catch arroja la excepción al programa que realiza la llamada. Con su ejemplo que contiene la declaración catch vacía, hay poca diferencia. Sin embargo, si en tu captura, haces algún procesamiento y arrojas el error, o incluso simplemente no tienes ninguna captura, finalmente se ejecutará.

31

Porque cuando esa única línea arroja una excepción, no lo sabría.

Con el primer bloque de código, la excepción será simplemente absorbida, el programa continuará ejecutándose incluso cuando el estado del programa sea incorrecto.

con el segundo bloque, la excepción será lanzada y se propaga hacia arriba, pero la reader.Close() todavía se garantiza la ejecución.

Si no se espera una excepción, entonces no ponga un bloque try..catch solo así, será difícil de depurar más tarde cuando el programa entró en mal estado y no tiene ni idea de por qué .

2

Finalmente es opcional: no hay motivo para tener un bloque "Finalmente" si no hay recursos para limpiar.

+2

Ahora que es un cubo de Rubik que puedo resolver. –

0

Entre muchas razones probablemente, las excepciones son muy lentas de ejecutar. Usted puede paralizar fácilmente sus tiempos de ejecución si esto sucede mucho.

1

Tomado de: here

Elevación y excepciones de captura no debería producirse de forma rutinaria como parte de la ejecución exitosa de un método. Al desarrollar bibliotecas de clases, el código del cliente debe tener la oportunidad de probar una condición de error antes de emprender una operación que puede generar una excepción. Por ejemplo, System.IO.FileStream proporciona una propiedad CanRead que se puede verificar antes de llamar al método Read, lo que evita una posible excepción, como se ilustra en el siguiente fragmento de código:

Dim str As Stream = GetStream() Si (str.CanRead) Entonces 'código para leer flujo End If

la decisión de si se debe comprobar el estado de un objeto antes de invocar un método particular que puede provocar una excepción depende del estado de la esperada objeto. Si se crea un objeto FileStream utilizando una ruta de archivo que debería existir y un constructor que debe devolver un archivo en modo de lectura, no es necesario verificar la propiedad CanRead; la imposibilidad de leer FileStream sería una violación del comportamiento esperado de las llamadas a métodos realizadas, y se debe hacer una excepción. Por el contrario, si se documenta que un método devuelve una referencia de FileStream que puede o no ser legible, es recomendable verificar la propiedad CanRead antes de intentar leer datos.

Para ilustrar el impacto en el rendimiento que puede causar el uso de una técnica de codificación "ejecutar hasta la excepción", el rendimiento de un molde, que arroja una InvalidCastException si falla, se compara con el operador C# como, que devuelve nulos si el molde falla El rendimiento de las dos técnicas es idéntico para el caso donde el yeso es válido (ver Prueba 8.05), pero para el caso donde el yeso no es válido, y usar un yeso causa una excepción, usar un yeso es 600 veces más lento que usar el como operador (ver Prueba 8.06). El impacto de alto rendimiento de la técnica de lanzamiento de excepción incluye el costo de asignar, lanzar y atrapar la excepción y el costo de la posterior recolección de basura del objeto de excepción, lo que significa que el impacto instantáneo de arrojar una excepción no es tan alto. A medida que se emiten más excepciones, la recolección frecuente de basura se convierte en un problema, por lo que el impacto general del uso frecuente de una técnica de codificación de lanzamiento de excepción será similar a la Prueba 8.05.

+2

Scott: si el texto que citó arriba está detrás del muro de pago de expertsexchange.com, probablemente no deba publicarlo aquí. Puedo estar equivocado en esto, pero apostaría que no es una buena idea. –

0

El problema con los bloques try/catch que captan todas las excepciones es que su programa ahora está en un estado indeterminado si ocurre una excepción desconocida. Esto va completamente en contra de la regla de falla: no desea que su programa continúe si ocurre una excepción. El try/catch anterior podría atrapar OutOfMemoryExceptions, pero definitivamente es un estado en el que su programa no se ejecutará.

Los bloques Try/finally le permiten ejecutar el código de limpieza mientras sigue fallando rápidamente. Para la mayoría de las circunstancias, solo desea capturar todas las excepciones a nivel global, para que pueda iniciar sesión y luego salir.

20

Finalmente se ejecuta sin importar qué. Entonces, si tu bloque try fue exitoso, se ejecutará, si tu try block falla, ejecutará el bloque catch, y luego el bloque finally.

Además, es mejor tratar de utilizar la construcción siguiente:

using (StreamReader reader=new StreamReader("myfile.txt")) 
{ 
} 

Como la instrucción using se envuelve de forma automática en un try/finally y la corriente se cerrará automáticamente. (Necesitará poner a prueba/capturar la declaración de uso si quiere capturar la excepción).

+4

Esto no es correcto. El uso no ajusta el código con try/catch, debería decir try/finally – pr0nin

3

El bloque try..finally arrojará todas las excepciones que se produzcan. Todo finally hace es asegurarse de que el código de limpieza se ejecuta antes de que se produzca la excepción.

La try..catch con un catch vacío consume por completo cualquier excepción y oculta el hecho de que sucedió. El lector estará cerrado, pero no se sabe si sucedió lo correcto. ¿Qué pasa si su intención era escribir i en el archivo? En este caso, no accederá a esa parte del código y myfile.txt estará vacío. ¿Manejan todos los métodos indirectos esto correctamente? Cuando vea el archivo vacío, ¿podrá adivinar correctamente que está vacío porque se lanzó una excepción? Mejor arrojar la excepción y dejar que se sepa que estás haciendo algo mal.

Otra razón es que try..catch hecho así es completamente incorrecto. Lo que dices al hacer esto es: "Pase lo que pase, puedo manejarlo". ¿Qué pasa con StackOverflowException, puede limpiar después de eso? ¿Qué hay de OutOfMemoryException? En general, solo debe manejar las excepciones que espera y sabe cómo manejar.

1

Bueno, es una mala práctica detectar excepciones que no te molestas en manejar. Consulte Chapter 5 about .Net Performance desde Mejorando .NET Application Performance and Scalability. Nota al margen, probablemente debería estar cargando la secuencia dentro del bloque try, de esa manera, puede detectar la excepción pertinente si falla. Crear la secuencia fuera del bloque try frustra su propósito.

0

La diferencia efectiva entre los ejemplos es insignificante siempre que no se generen excepciones.

Sin embargo, si se lanza una excepción mientras se encuentra en la cláusula 'try', el primer ejemplo se tragará por completo. El segundo ejemplo elevará la excepción al siguiente escalón de la pila de llamadas, por lo que la diferencia en los ejemplos indicados es que se oscurece por completo cualquier excepción (primer ejemplo), y el otro (segundo ejemplo) retiene información de excepción para un posible manejo posterior mientras sigue ejecutando el contenido en la cláusula 'finally'.

Si, por ejemplo, pusiera código en la cláusula 'catch' del primer ejemplo que arrojó una excepción (la que se planteó inicialmente, o una nueva), el código de limpieza del lector nunca se ejecutará . Finalmente ejecuta independientemente de lo que sucede en la cláusula 'catch'.

Por lo tanto, la principal diferencia entre 'catch' y 'finally' es que el contenido del bloque 'finally' (con unas raras excepciones) puede considerarse garantizado para ejecutar, incluso frente a un imprevisto excepción, mientras que cualquier código que siga una cláusula 'catch' (pero fuera de una cláusula 'finally') no conllevará tal garantía.

Incidentalmente, Stream y StreamReader implementan IDisposable, y se pueden envolver en un bloque 'using'. 'El uso de' bloques son el equivalente semántico de try/finally (sin 'captura'), por lo que su ejemplo podría expresarse más concisamente como:

using (StreamReader reader = new StreamReader("myfile.txt")) 
{ 
    int i = 5/0; 
} 

... que se cerrará y disponer de la instancia StreamReader cuando sale del alcance. Espero que esto ayude.

5

estoy de acuerdo con lo que parece ser el consenso aquí - un vacío 'captura' es malo porque enmascara cualquier excepción podrían haber ocurrido en el bloque try.

Además, desde el punto de vista de la legibilidad, cuando veo un bloque de "prueba", supongo que habrá una declaración de "captura" correspondiente. Si sólo está utilizando un 'intento' con el fin de asegurar que los recursos son de-asignados en el bloque 'finalmente', usted podría considerar la 'using' statement lugar:

using (StreamReader reader = new StreamReader('myfile.txt')) 
{ 
    // do stuff here 
} // reader.dispose() is called automatically 

Usted puede utilizar el 'utilizar' comunicado con cualquier objeto que implementa IDisposable. El método de eliminación del objeto() se llama automáticamente al final del bloque.

6

Si bien los siguientes 2 bloques de código son equivalentes, no son iguales.

try 
{ 
    int i = 1/0; 
} 
catch 
{ 
    reader.Close(); 
    throw; 
} 

try 
{ 
    int i = 1/0; 
} 
finally 
{ 
    reader.Close(); 
} 
  1. 'finalmente' el código de intención de revelador. Usted declara al compilador y a otros programadores que este código debe ejecutarse sin importar qué.
  2. si tiene varios bloques catch y tiene código de limpieza, necesita finalmente. Sin fin, estarías duplicando tu código de limpieza en cada bloque catch. (Principio DRY)

finalmente se hace son especiales. El CLR reconoce y trata el código dentro de un bloque finally por separado de los bloques catch, y el CLR hace todo lo posible para garantizar que siempre se ejecute un bloque finally. No es solo azúcar sintáctica del compilador.

0

try {...} catch {} no siempre es malo. No es un patrón común, pero tiendo a usarlo cuando necesito cerrar recursos sin importar nada, como cerrar un (posible) socket abierto al final de un hilo.

2

Es una mala práctica agregar una cláusula catch solo para volver a lanzar la excepción.

0

Si usted leerá C# for programmers usted entenderá, que el bloque finally era de diseño para optimizar la solicitud y evitar pérdida de memoria.

El CLR no elimina por completo las fugas ...pérdidas de memoria pueden ocurrir si el programa sin querer mantener las referencias a objetos no deseados

Por ejemplo cuando se abre una conexión de archivo o base de datos, el equipo le puede asignar memoria para atender esa transacción, y que la memoria se mantiene no menos que el Eliminado o se ejecutó el comando de cierre. pero si durante la transacción, se produjo un error, el comando anterior terminará no a menos que estuviera dentro del bloque try.. finally...

catch era diferente de finally en el sentido de que, catch fue diseñado para darle la forma de manejar/gestionar o interpretar el error en sí mismo. Piense en ello como una persona que le dice "hey atrapé a algunos tipos malos, ¿qué quieres que les haga?" mientras que finally fue diseñado para asegurarse de que sus recursos se colocaron correctamente. Piense en alguien que, ya sea que haya o no algunos tipos malos, se asegurará de que su propiedad aún esté a salvo.

Y debe permitir que esos dos trabajen juntos para siempre.

por ejemplo:

try 
{ 
    StreamReader reader=new StreamReader("myfile.txt"); 
    //do other stuff 
} 
catch(Exception ex){ 
// Create log, or show notification 
generic.Createlog("Error", ex.message); 
} 
finally // Will execute despite any exception 
{ 
    reader.Close(); 
} 
2

Uso Try..Catch..Finally, si su método sabe cómo manejar la excepción localmente. La excepción se produce en Try, Handled in Catch y después de eso, la limpieza se realiza en Finally.

En caso de que el método no sabe cómo manejar la excepción, pero necesita una limpieza, una vez que se ha producido el uso Try..Finally

Por esta excepción se propaga a los métodos de llamada y manipulados si hay alguna cuota adecuadas declaraciones en los métodos de llamada. Si no hay controladores de excepción en el método actual o cualquiera de los métodos de llamada, la aplicación falla.

Por Try..Finally se garantiza que la limpieza local se realiza antes de propagar la excepción a los métodos de llamada.