Pregunta

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

De lo contrario, ¿qué tiene de especial finalmente?

¿Fue útil?

Solución

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 ella.

Otros consejos

" 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.

" Captura " es una declaración de "Puedo recuperarme de esta excepción". Solo debe recuperarse de las excepciones que realmente puede corregir: capturar sin argumentos dice "¡Hola, 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, 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 informados se ocupen del 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 programa continuará ejecutándose incluso cuando el estado del programa sea incorrecto.

Con el segundo bloque, la excepción se lanzará y aparecerá pero el reader.Close () todavía se garantiza que se ejecute.

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 idea de por qué.

Finalmente se ejecuta sin importar qué. 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:

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

Como la declaración de uso se ajusta automáticamente en un intento / finalmente y la secuencia se cerrará automáticamente. (Tendrá que poner un try / catch alrededor de la instrucción de uso si realmente 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 intenciones. 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 un código de limpieza, finalmente necesita. Sin finalmente, estaría duplicando su código de limpieza en cada bloque catch. (Principio SECO)

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.

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 el 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 dispose () del objeto se llama automáticamente al final del bloque.

El bloque try..finally aún arrojará cualquier excepción que se genere. Todo lo que finalmente 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é sucede 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é pasa con StackOverflowException , puedes limpiar después de eso? ¿Qué pasa con OutOfMemoryException ? En general, solo debe manejar las excepciones que espera y sabe cómo manejar.

Use 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 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 declaraciones Catch adecuadas 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 se asegura que la limpieza local se realice antes de propagar la excepción a los métodos de llamada.

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.

Debería tener una declaración finalmente allí en caso de que haya una excepción, de modo que pueda limpiar los recursos antes de que esa llamada sea lanzada a la persona que llama.

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

Además, las declaraciones 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 razón para tener un " Finalmente " bloquear si no hay recursos para limpiar.

Tomado de: aquí

Las excepciones de aumento y captura 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) Entonces   'código para leer la secuencia Fin si

La decisión de verificar el estado de un objeto antes de invocar un método en 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 el uso de " ejecutar hasta excepción " La técnica de codificación puede causar que el rendimiento de un lanzamiento, que arroja una InvalidCastException si el lanzamiento falla, 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, 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 excepciones 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.

Con finalmente, puede limpiar recursos, incluso si su instrucció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, finalmente se ejecutará.

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

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

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 Try / finally le permiten ejecutar el código de limpieza sin fallar 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 sus ejemplos es insignificante siempre que no se produzcan excepciones.

Sin embargo, si se produce una excepción 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 la información de excepción para un posible manejo posterior mientras sigue 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 ejecuta independientemente de lo que sucede 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 ejecutar, incluso en el ante una excepción inesperada, mientras que cualquier código que siga a una cláusula 'catch' (pero fuera de una cláusula 'finalmente') no conllevaría tal garantía.

Por cierto, Stream y StreamReader implementan IDisposable y se pueden envolver en un bloque 'using'. 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:

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

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

pruebe {& # 8230;} 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top