Por qué try {…} finalmente {…} es bueno; prueba {…} catch {} ¿malo?

He visto a personas decir que está mal usar captura sin argumentos, especialmente si esa captura no hace nada:

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 ejecutar, pero el código después de try..catch no lo hará).

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

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.

“Finalmente” es una afirmación de “Algo que siempre debes hacer para asegurarte de que el estado del progtwig sea sensato”. Como tal, siempre es una buena forma tener uno, si hay alguna posibilidad de que las excepciones puedan descartar el estado del progtwig. El comstackdor también hace grandes esfuerzos para asegurarse de que se ejecute su código Finally.

“Catch” es una statement 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 sería realmente una discusión semántica, sobre 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.

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

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

Con el segundo bloque, se lanzará la excepción y reader.Close() pero el reader.Close() aún está garantizado para ejecutarse.

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

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 intentar usar la siguiente construcción:

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

Como la instrucción using se ajusta automáticamente en try / finally y la transmisión se cerrará automáticamente. (Necesitará poner a prueba / capturar la statement de uso si quiere capturar la excepción).

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’ es un código que revela la intención. Usted declara al comstackdor y a otros progtwigdores que este código debe ejecutarse sin importar qué.
  2. si tiene varios bloques catch y tiene código de limpieza, finalmente necesita. Sin fin, estarías duplicando tu código de limpieza en cada bloque catch. (Principio DRY)

finalmente los bloques 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 comstackdor.

Estoy de acuerdo con lo que parece ser el consenso aquí: una “captura” vacía es mala porque enmascara cualquier excepción que pueda 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 statement de “captura” correspondiente. Si solo está utilizando una ‘prueba’ para asegurarse de que los recursos se desasignan en el bloque ‘finally’, podría considerar la instrucción ‘using’ en su lugar:

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

Puede usar la instrucción ‘using’ con cualquier objeto que implemente IDisposable. El método de eliminación del objeto () se llama automáticamente al final del bloque.

El bloque try..finally arrojará todas las excepciones que se planteen. Todo lo que hace finally es garantizar que el código de limpieza se ejecute antes de lanzar la excepción.

El try..catch con un catch vacío consumirá por completo cualquier excepción y ocultará el hecho de que sucedió. El lector estará cerrado, pero no se sabe si sucedió lo correcto. ¿Qué pasa si tu 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é pasa con OutOfMemoryException ? En general, solo debe manejar las excepciones que espera y sabe cómo manejar.

Si no sabe qué tipo de excepción capturar o qué hacer con ella, no tiene sentido tener una statement 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.

Debería tener una statement final allí en caso de que haya una excepción, para que pueda limpiar los recursos antes de que se envíe esa excepción a la persona que llama.

Desde una perspectiva de legibilidad, está diciendo 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 catch vacías tienden a tener un cierto “olor” a ellos. Pueden ser una señal de que los desarrolladores no están pensando en las diversas excepciones que pueden ocurrir y cómo manejarlas.

Finalmente, es opcional: no hay motivo para tener un bloque “Finalmente” si no hay recursos para limpiar.

Tomado de: aquí

El aumento y la captura de excepciones no deben ocurrir rutinariamente 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 () If (str.CanRead) Then ‘código para leer la secuencia End If

La decisión de verificar el estado de un objeto antes de invocar un método particular que pueda generar una excepción depende del estado esperado del 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 encoding “ejecutar hasta la excepción”, el rendimiento de un lanzamiento, que arroja una InvalidCastException si el lanzamiento falla, se compara con el operador C # como, que devuelve nulos si falla un lanzamiento. 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 encoding de lanzamiento de excepción será similar a la Prueba 8.05.

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

Use 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 su método no sepa cómo manejar la excepción pero necesita una limpieza una vez que se haya producido, use Try..Finally

Con esto, la excepción se propaga a los métodos de llamada y se maneja si hay alguna instrucción Catch adecuada 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..FinallyTry..Finally , se garantiza que la limpieza local se realice antes de propagar la excepción a los métodos de llamada.

Finalmente, puede limpiar los recursos, incluso si su instrucción catch arroja la excepción al progtwig que realiza la llamada. Con su ejemplo que contiene la statement 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á.

Bueno, para empezar, es una mala práctica detectar excepciones que no te molestas en manejar. Consulte el Capítulo 5 sobre .Net Performance de la mejora del rendimiento y la escalabilidad de la aplicación .NET . 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.

Si va a leer C # para los progtwigdores , comprenderá que el bloque finally fue diseñado para optimizar una aplicación y evitar la pérdida de memoria.

El CLR no elimina por completo las fugas … pueden producirse pérdidas de memoria si el progtwig mantiene inadvertidamente referencias a objetos no deseados

Por ejemplo, cuando abre un archivo o una conexión de base de datos, su máquina asignará memoria para atender esa transacción, y esa memoria se mantendrá no a menos que se ejecute el comando de eliminación o 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 fue diferente de finally en el sentido de que catch fue diseñado para darle la forma de manejar / administrar 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 estuvieran correctamente ubicados. 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 deberías 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(); } 

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.

El problema con los bloques try / catch que captan todas las excepciones es que su progtwig 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 progtwig continúe si ocurre una excepción. El try / catch anterior podría atrapar OutOfMemoryExceptions, pero definitivamente es un estado en el que su progtwig no se ejecutará.

Los bloques Try / finally le permiten ejecutar el código de limpieza mientras aún falla 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.

La diferencia efectiva entre tus 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 stack 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 se ejecuta independientemente de lo que ocurra en la cláusula ‘catch’.

Por lo tanto, la principal diferencia entre ‘catch’ y ‘finally’ es que el contenido del bloque ‘finally’ (con algunas raras excepciones) se puede considerar que se ejecuta de forma segura , incluso frente a una excepción inesperada, mientras que cualquier código siguiente una cláusula ‘catch’ (pero fuera de una cláusula ‘finally’) no conllevaría tal garantía.

Incidentalmente, Stream y StreamReader implementan IDisposable, y pueden ser envueltos en un bloque ‘using’. Los bloques ‘Using’ son el equivalente semántico de try / finally (no ‘catch’), por lo que su ejemplo podría expressse de forma más laxa como:

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

… que cerrará y eliminará la instancia de StreamReader cuando se salga del scope. Espero que esto ayude.

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.