He visto (y mantenido) varias opiniones fuertes sobre esto. La respuesta es que no creo que actualmente haya un enfoque ideal en C#.
En un momento sentí que (en una forma de Java) la excepción es parte de la interfaz binaria de un método, tanto como el tipo de retorno y los tipos de parámetros. Pero en C#, simplemente no lo es. Esto está claro por el hecho de que no hay un sistema de especificación de lanzamientos.
En otras palabras, puede, si lo desea, adoptar la actitud de que solo sus tipos de excepción salgan volando de los métodos de su biblioteca, para que sus clientes no dependan de los detalles internos de su biblioteca. Pero pocas bibliotecas se molestan en hacer esto.
El consejo oficial del equipo de C# es captar cada tipo específico que pueda arrojarse con un método, si cree que puede manejarlos. No atrape nada que no pueda manejar realmente. Esto implica que no hay encapsulamiento de excepciones internas en los límites de la biblioteca.
Pero a su vez, eso significa que necesita una documentación perfecta de lo que podría arrojar un método determinado. Las aplicaciones modernas se basan en montones de bibliotecas de terceros, que evolucionan rápidamente. Se burla de tener un sistema de tipado estático si todos intentan atrapar tipos de excepción específicos que podrían no ser correctos en futuras combinaciones de versiones de bibliotecas, sin verificación en tiempo de compilación.
Así que la gente haga lo siguiente:
try
{
}
catch (Exception x)
{
// log the message, the stack trace, whatever
}
El problema es que este atrapa todo tipo de excepción, incluidas las que indican fundamentalmente un problema grave, como una excepción de referencia nula. Esto significa que el programa está en un estado desconocido. En el momento que se detecta, debe cerrarse antes de que haga algún daño a los datos persistentes del usuario (comienza a destruir archivos, registros de bases de datos, etc.).
El problema oculto aquí es intentar/finalmente. Es una característica de gran lenguaje, de hecho es esencial, pero si una excepción suficientemente seria está volando en la pila, ¿realmente debería estar causando que finalmente se ejecuten los bloques? ¿Realmente quieres que la evidencia se destruya cuando hay un error en el progreso?Y si el programa está en un estado desconocido, cualquier cosa importante podría ser destruida por esos bloques finally.
Así que lo que realmente quiere es (actualizado para C# 6!):
try
{
// attempt some operation
}
catch (Exception x) when (x.IsTolerable())
{
// log and skip this operation, keep running
}
En este ejemplo, podría escribir IsTolerable
como un método de extensión en Exception
que devuelve false
si la excepción interna es NullReferenceException
, IndexOutOfRangeException
, InvalidCastException
o cualquier otro tipo de excepción que usted ha decidido debe indicar un error de bajo nivel que debe detener la ejecución y requerir una investigación. Estas son situaciones "intolerables".
Esto podría denominarse manejo de excepciones "optimista": suponiendo que todas las excepciones son tolerables excepto por un conjunto de tipos de lista negra conocidos. La alternativa (respaldada por C# 5 y anterior) es el enfoque "pesimista", donde solo las excepciones conocidas de la lista blanca se consideran tolerables y cualquier otra cosa no se controla.
Hace años, el enfoque pesimista era la postura oficial recomendada. Pero en estos días, el CLR detecta todas las excepciones en Task.Run
, por lo que puede mover errores entre hilos. Esto hace que finalmente se ejecuten bloques. Por lo tanto, la CRL es muy optimista de forma predeterminada.
También puede contar con el evento AppDomain.UnhandledException, ahorrar tanta información como sea posible para fines de apoyo (al menos el seguimiento de pila) y luego llamar a Environment.FailFast para apagar el proceso antes de cualquier finally
bloques pueden ejecutar (que podría destruir la valiosa la información necesaria para investigar el error, o arrojar otras excepciones que oculten el original).
Su primer ejemplo es un antipatrón para C#, use 'throw;' en su lugar o bien está restableciendo la traza de pila al punto de proyección actual. Solo usar 'throw;' mantendrá la traza original de la pila. –
Doble duplicado: http://stackoverflow.com/questions/178456/what-is-the-proper-way-to-re-throw-an-exception-in-c y http://stackoverflow.com/questions/ 22623/net-throwing-exceptions-best-practices. –
Tenga en cuenta que puede mantener el seguimiento de la pila incluso si cambia el tipo de excepción mediante catch (Exception ex) {throw new MyCustomException (ex); } si utiliza el constructor proporcionado de la clase Exception en su clase MyCustomException (como lo hacen todos los tipos de excepciones .NET). – dbemerlin