He visto a personas decir que es una mala forma usar catch sin argumentos, especialmente si ese catch 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 el código de limpieza en un bloque finalmente y poner el código de limpieza después de los bloques try..catch es si tiene declaraciones de retorno en su bloque try (en ese caso, el código de limpieza finalmente será ejecutar, pero el código después del intento ... captura no lo hará).
De lo contrario, ¿qué tiene de especial finalmente?
c#
.net
exception-handling
try-catch
try-catch-finally
mbeckish
fuente
fuente
Respuestas:
La gran diferencia es que
try...catch
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 él.fuente
"Finalmente" es una declaració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 alterar el estado del programa. El compilador también hace todo lo posible para garantizar que se ejecute su código de Finalmente.
"Capturar" es una declaración de "Puedo recuperarme de esta excepción". Solo debe recuperarse de las excepciones que realmente puede corregir: la captura sin argumentos dice "¡Hola, puedo recuperarme de cualquier cosa!", Lo que casi siempre es falso.
Si fuera posible recuperarse de cada excepción, entonces realmente sería una objeción semántica, sobre lo que estás declarando tu intención de ser. Sin embargo, no lo es, y casi con certeza los marcos superiores al suyo estarán mejor equipados para manejar ciertas excepciones. Como tal, use finalmente, haga que su código de limpieza se ejecute de forma gratuita, pero aún así permita que manejadores más expertos manejen el problema.
fuente
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 se absorberá , 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 el
reader.Close()
todavía se garantiza la ejecución.Si no se espera una excepción, entonces no intentes el bloque try..catch, será difícil de depurar más tarde cuando el programa entró en mal estado y no tienes idea de por qué.
fuente
Finalmente se ejecuta pase lo que pase. Entonces, si su bloque try fue exitoso, se ejecutará, si su bloque try falla, entonces ejecutará el bloque catch y luego el bloque finalmente.
Además, es mejor intentar usar la siguiente construcción:
Como la declaración de uso se ajusta automáticamente en un intento / finalmente y la secuencia se cerrará automáticamente. (Necesitará poner un try / catch alrededor de la instrucción de uso si realmente quiere capturar la excepción).
fuente
Si bien los siguientes 2 bloques de código son equivalentes, no son iguales.
Finalmente los bloques son especiales. El CLR reconoce y trata el código dentro de un bloque finalmente separado de los bloques catch, y el CLR hace todo lo posible para garantizar que un bloque finalmente siempre se ejecute. No es solo azúcar sintáctico del compilador.
fuente
Estoy de acuerdo con lo que parece ser el consenso aquí: una 'captura' vacía es mala porque oculta 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 'intentar', supongo que habrá una declaración 'catch' correspondiente. Si solo está usando un 'intento' para asegurarse de que los recursos se desasignen en el bloque 'finalmente', puede considerar la declaración 'usar' en su lugar:
Puede usar la instrucción 'using' con cualquier objeto que implemente IDisposable. El método dispose () del objeto se llama automáticamente al final del bloque.
fuente
Utilizar
Try..Catch..Finally
, si su método sabe cómo manejar la excepción localmente. La excepción ocurre en Try, Handled in Catch y después de eso, la limpieza se realiza en Finalmente.En caso de que su método no sepa cómo manejar la excepción pero necesita una limpieza una vez que ha ocurrido, use
Try..Finally
De este modo, la excepción se propaga a los métodos de llamada y se maneja si hay alguna declaración Catch adecuada en los métodos de llamada. Si no hay controladores de excepción en el método actual o en ninguno de los métodos de llamada, la aplicación se bloquea.
Por
Try..Finally
ello se garantiza que las tareas de limpieza local, se hace antes de la propagación de la excepción a los métodos de llamada.fuente
El bloque try..finally aún arrojará cualquier excepción que se genere. Todas
finally
hace es asegurarse de que el código de limpieza se ejecute antes de que se genere la excepción.El try..catch con una captura vacía 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 su intención era escribir i en el archivo? En este caso, no llegará a esa parte del código y myfile.txt estará vacío. ¿Todos los métodos posteriores manejan esto correctamente? Cuando vea el archivo vacío, ¿podrá adivinar correctamente que está vacío porque se produjo una excepción? Es mejor lanzar la excepción y dejar que se sepa que estás haciendo algo mal.
Otra razón es el try..catch hecho así es completamente incorrecto. Lo que estás diciendo al hacer esto es: "No importa lo que pase, puedo manejarlo". ¿Qué tal
StackOverflowException
, puedes limpiar después de eso? ¿Qué hay deOutOfMemoryException
? En general, solo debe manejar las excepciones que espera y sabe cómo manejar.fuente
Si no sabe qué tipo de excepción atrapar o qué hacer con él, no tiene sentido tener una declaración catch. Simplemente debe dejarlo para que una persona que llama desde arriba tenga más información sobre la situación para saber qué hacer.
Todavía debe tener una declaración finalmente allí en caso de que haya una excepción, para que pueda limpiar los recursos antes de que esa excepción sea lanzada a la persona que llama.
fuente
Desde una perspectiva de legibilidad, es más explícito decirle a los futuros lectores de códigos "estas cosas aquí son importantes, deben hacerse sin importar lo que pase". Esto es bueno.
Además, las declaraciones de captura vacías tienden a tener un cierto "olor". Pueden ser una señal de que los desarrolladores no están pensando en las diversas excepciones que pueden ocurrir y cómo manejarlas.
fuente
Finalmente es opcional: no hay ninguna razón para tener un bloque "Finalmente" si no hay recursos para limpiar.
fuente
Tomado de: aquí
El aumento y la captura de excepciones no deberían 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 pueda generar una excepción. Por ejemplo, System.IO.FileStream proporciona una propiedad CanRead que se puede verificar antes de llamar al método Read, evitando que se genere una posible excepción, como se ilustra en el siguiente fragmento de código:
Dim str As Stream = GetStream () If (str.CanRead) Then 'code to read stream End If
La decisión de verificar el estado de un objeto antes de invocar un método particular que puede 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 debería devolver un archivo en modo de lectura, no es necesario verificar la propiedad CanRead; la imposibilidad de leer el FileStream sería una violación del comportamiento esperado de las llamadas al método realizadas, y se debería plantear 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 los datos.
Para ilustrar el impacto en el rendimiento que puede causar el uso de una técnica de codificación de "ejecución hasta excepción", el rendimiento de un lanzamiento, que arroja una InvalidCastException si falla el lanzamiento, se compara con el C # como operador, que devuelve nulos si falla un lanzamiento. El rendimiento de las dos técnicas es idéntico para el caso donde el lanzamiento es válido (ver Prueba 8.05), pero para el caso donde el lanzamiento es inválido, y usar un lanzamiento causa una excepción, usar un lanzamiento es 600 veces más lento que usar el lanzamiento como operador (ver Prueba 8.06). El impacto de alto rendimiento de la técnica de lanzamiento de excepciones incluye el costo de asignación, lanzamiento y captura de 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 lanzar una excepción no es tan alto. A medida que se lanzan más excepciones,
fuente
Es una mala práctica agregar una cláusula catch solo para volver a lanzar la excepción.
fuente
Si lee C # para programadores , comprenderá que el bloque finalmente fue diseñado para optimizar una aplicación y evitar pérdidas de memoria.
Por ejemplo, cuando abre una conexión de archivo o base de datos, su máquina asignará memoria para atender esa transacción, y esa memoria se mantendrá no a menos que se haya ejecutado el comando de eliminación o cierre. pero si durante la transacción se produjo un error, el comando en curso se cancelará a menos que se encuentre dentro del
try.. finally..
bloque.catch
era diferentefinally
en el sentido de que, catch fue diseñado para darle forma de manejar / gestionar o interpretar el error por sí mismo. Piense en ello como una persona que le dice "oye, atrapé a algunos tipos malos, ¿qué quieres que les haga?" mientrasfinally
fue diseñado para asegurarse de que sus recursos se ubicaron correctamente. Piense en alguien que si hay o no algunos tipos malos se asegurará de que su propiedad aún esté segura.Y deberías permitir que esos dos trabajen juntos para siempre.
por ejemplo:
fuente
Finalmente, puede limpiar recursos, incluso si su declaración catch arroja la excepción al programa de llamada. Con su ejemplo que contiene la declaración catch vacía, hay poca diferencia. Sin embargo, si en su captura, realiza un procesamiento y arroja el error, o incluso simplemente no tiene ninguna captura, el finalmente todavía se ejecutará.
fuente
Bueno, para empezar, es una mala práctica detectar excepciones que no te molestas en manejar. Consulte el Capítulo 5 sobre .Net Performance al mejorar el rendimiento y la escalabilidad de las aplicaciones .NET . Nota al margen, probablemente debería estar cargando la secuencia dentro del bloque de prueba, de esa manera, puede atrapar la excepción pertinente si falla. Crear la secuencia fuera del bloque try frustra su propósito.
fuente
Probablemente, entre muchas razones, las excepciones son muy lentas de ejecutar. Puede paralizar fácilmente sus tiempos de ejecución si esto sucede mucho.
fuente
El problema con los bloques try / catch que capturan 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 rápida: no desea que su programa continúe si ocurre una excepción. El try / catch anterior incluso capturaría OutOfMemoryExceptions, pero ese es definitivamente un estado en el que su programa no se ejecutará.
Los bloques de prueba / finalmente le permiten ejecutar código de limpieza sin fallar rápidamente. Para la mayoría de las circunstancias, solo desea capturar todas las excepciones a nivel global, de modo que pueda iniciar sesión y luego salir.
fuente
La diferencia efectiva entre sus ejemplos es insignificante siempre que no se produzcan excepciones.
Sin embargo, si se lanza una excepción mientras está en la cláusula 'try', el primer ejemplo la tragará por completo. El segundo ejemplo elevará la excepción al siguiente paso en la pila de llamadas, por lo que la diferencia en los ejemplos indicados es que uno oculta por completo cualquier excepción (primer ejemplo) y el otro (segundo ejemplo) retiene información de excepción para un posible manejo posterior mientras aún ejecutando el contenido en la cláusula 'finalmente'.
Si, por ejemplo, tuviera que poner código en la cláusula 'catch' del primer ejemplo que arrojó una excepción (ya sea la que se planteó inicialmente o una nueva), el código de limpieza del lector nunca se ejecutaría. Finalmente se ejecuta independientemente de lo que ocurra en la cláusula 'catch'.
Entonces, la principal diferencia entre 'atrapar' y 'finalmente' es que el contenido del bloque 'finalmente' (con algunas raras excepciones) puede considerarse garantizado para ejecutarse, incluso ante una excepción inesperada, mientras que cualquier código sigue una cláusula de "captura" (pero fuera de una cláusula de "finalmente") no conllevaría tal garantía.
Por cierto, Stream y StreamReader implementan IDisposable y se pueden envolver en un bloque de "uso". Los bloques 'usar' son el equivalente semántico de try / finally (no 'catch'), por lo que su ejemplo podría expresarse más tersamente como:
... que cerrará y eliminará la instancia de StreamReader cuando salga del alcance. Espero que esto ayude.
fuente
intentar {…} catch {} no siempre es malo. No es un patrón común, pero tiendo a usarlo cuando necesito cerrar recursos sin importar qué, como cerrar un (posiblemente) enchufes abiertos al final de un hilo.
fuente