Mientras mantengo el código de mi colega incluso de alguien que dice ser un desarrollador senior, a menudo veo el siguiente código:
try
{
//do something
}
catch
{
//Do nothing
}
o a veces escriben información de registro en archivos de registro como el siguiente try catch
bloque
try
{
//do some work
}
catch(Exception exception)
{
WriteException2LogFile(exception);
}
Me pregunto si lo que han hecho es la mejor práctica. Me confunde porque, en mi opinión, los usuarios deben saber qué sucede con el sistema.
Por favor, dame un consejo.
NullReference
oArgumentNull
que no forma parte del flujo de la aplicación), significa que hay un error que debe corregirse, por lo que registrarlo ayudará a depurar su código mucho más rápido.Respuestas:
Mi estrategia de manejo de excepciones es:
Para capturar todas las excepciones no controladas conectándose a la
Application.ThreadException event
, luego decida:Luego siempre adjunto cada pieza de código que se ejecuta externamente en
try/catch
:Luego lo adjunto en 'try / catch'
ApplicationException("custom message", innerException)
para hacer un seguimiento de lo que realmente sucedióAdemás, hago todo lo posible para ordenar las excepciones correctamente . Hay excepciones que:
finally
sección durante unTreeView
llenado)Al usuario no le importa, pero es importante saber qué sucedió. Así que siempre los registro:
Es una buena práctica diseñar algunos métodos estáticos para manejar excepciones en los controladores de errores de nivel superior de la aplicación.
También me obligo a intentar:
Así que finalmente :
Malo:
Inútil:
Intentar finalmente sin una trampa es perfectamente válido:
Lo que hago en el nivel superior:
Lo que hago en algunas funciones llamadas:
Hay mucho que ver con el manejo de excepciones (Excepciones personalizadas), pero esas reglas que trato de tener en cuenta son suficientes para las aplicaciones simples que hago.
Aquí hay un ejemplo de métodos de extensiones para manejar excepciones atrapadas de una manera cómoda. Se implementan de una manera que se pueden encadenar, y es muy fácil agregar su propio procesamiento de excepciones capturadas.
fuente
catch(Exception ex) { throw ex; }
en C # es peor que redundante (independientemente del tipo de excepción que esté detectando). Para volver a lanzar, usethrow;
. Con el primero, la excepción parecerá que se originó a partir de su,throw ex
mientras que con el último, se originará correctamente a partir de lathrow
declaración original .Application.ThreadException
evento y envuelves cada excepción con uncatch(Exception ex) {ex.Log(ex);}
. Probablemente estaría de acuerdo en que la primera es una práctica excelente, pero la segunda agrega el riesgo de duplicar sus registros de errores y oculta que ocurrió la excepción. Tambiénthrow ex
es muy muy malo.Application.ThreadException
evento, no estaba al tanto de eso, muy útil.La mejor práctica es que el manejo de excepciones nunca debe ocultar problemas . Esto significa que los
try-catch
bloques deberían ser extremadamente raros.Hay 3 circunstancias en las que usar un
try-catch
tiene sentido.Trate siempre con las excepciones conocidas lo más bajo posible. Sin embargo, si espera una excepción, por lo general, es una mejor práctica probarla primero. Por ejemplo, las excepciones de análisis, formato y aritmética casi siempre se manejan mejor mediante verificaciones lógicas primero, en lugar de una específica
try-catch
.Si necesita hacer algo en una excepción (por ejemplo, iniciar sesión o revertir una transacción), vuelva a lanzar la excepción.
Siempre trate con excepciones desconocidas tan alto como sea posible: el único código que debería consumir una excepción y no volver a lanzarlo debería ser la interfaz de usuario o la API pública.
Suponga que se está conectando a una API remota, aquí sabe que espera ciertos errores (y tiene que hacerlo en esas circunstancias), así que este es el caso 1:
Tenga en cuenta que no se detectan otras excepciones, ya que no se esperan.
Ahora suponga que está intentando guardar algo en la base de datos. Tenemos que revertirlo si falla, por lo que tenemos el caso 2:
Tenga en cuenta que volvemos a lanzar la excepción: el código más arriba aún necesita saber que algo ha fallado.
Finalmente tenemos la interfaz de usuario: aquí no queremos tener excepciones completamente no controladas, pero tampoco queremos ocultarlas. Aquí tenemos un ejemplo del caso 3:
Sin embargo, la mayoría de los marcos de API o UI tienen formas genéricas de hacer el caso 3. Por ejemplo, ASP.Net tiene una pantalla de error amarilla que vuelca los detalles de la excepción, pero que puede reemplazarse con un mensaje más genérico en el entorno de producción. Seguirlas es una práctica recomendada porque le ahorra mucho código, pero también porque el registro de errores y la visualización deben ser decisiones de configuración en lugar de codificadas.
Todo esto significa que el caso 1 (excepciones conocidas) y el caso 3 (manejo único de la IU) tienen mejores patrones (evite el error esperado o el manejo manual de errores en la IU).
Incluso el caso 2 puede ser reemplazado por mejores patrones, por ejemplo, los ámbitos de transacción (
using
bloques que deshacen cualquier transacción que no se haya comprometido durante el bloque) dificultan que los desarrolladores obtengan el patrón de mejores prácticas incorrecto.Por ejemplo, suponga que tiene una aplicación ASP.Net a gran escala. El registro de errores puede realizarse a través de ELMAH , la visualización de errores puede ser un YSoD informativo localmente y un buen mensaje localizado en producción. Las conexiones a la base de datos pueden realizarse mediante ámbitos y
using
bloques de transacciones . No necesitas un solotry-catch
bloque.TL; DR: La mejor práctica es no utilizar
try-catch
bloques en absoluto.fuente
try-catch
: puede (muy ocasionalmente) ser útil y no estoy discutiendo que nunca debas usarlos, pero el 99% de las veces hay una mejor manera.Una excepción es un error de bloqueo .
En primer lugar, la mejor práctica debería ser no lanzar excepciones para ningún tipo de error, a menos que sea un error de bloqueo .
Si el error está bloqueando , arroje la excepción. Una vez que se lanza la excepción, no hay necesidad de ocultarla porque es excepcional; informe al usuario al respecto (debe volver a formatear toda la excepción a algo útil para el usuario en la interfaz de usuario).
Su trabajo como desarrollador de software es tratar de evitar un caso excepcional en el que algún parámetro o situación de tiempo de ejecución pueda terminar en una excepción. Es decir, las excepciones no deben silenciarse, pero deben evitarse .
Por ejemplo, si sabe que alguna entrada entera podría tener un formato no válido, use en
int.TryParse
lugar deint.Parse
. Hay muchos casos en los que puedes hacer esto en lugar de solo decir "si falla, simplemente lanza una excepción".Lanzar excepciones es costoso.
Si, después de todo, se lanza una excepción, en lugar de escribir la excepción en el registro una vez que se ha lanzado, una de las mejores prácticas es detectarla en un controlador de excepciones de primera oportunidad . Por ejemplo:
Mi postura es que los intentos / capturas locales son más adecuados para manejar casos especiales en los que puede traducir una excepción a otra, o cuando desea "silenciarla" por un caso muy, muy, muy, muy especial (un error de biblioteca arrojando una excepción no relacionada que necesita silenciar para solucionar todo el error).
Para el resto de los casos:
Respondiendo a @thewhiteambit por algún comentario ...
@thewhiteambit dijo:
En primer lugar, ¿cómo una excepción no puede ser ni siquiera un error?
null
mientras se esperaba el objeto => excepciónPodríamos enumerar 1k casos de cuando se lanza una excepción, y después de todo, cualquiera de los casos posibles será un error .
Una excepción es un error, porque al final del día es un objeto que recopila información de diagnóstico: tiene un mensaje y ocurre cuando algo sale mal.
Nadie lanzaría una excepción cuando no hay un caso excepcional. Las excepciones deberían ser errores de bloqueo porque una vez que se lanzan, si no intenta caer en el uso try / catch y las excepciones para implementar el flujo de control , significan que su aplicación / servicio detendrá la operación que entró en un caso excepcional .
Además, sugiero a todos que verifiquen el paradigma de falla rápida publicado por Martin Fowler (y escrito por Jim Shore) . Así es como siempre entendí cómo manejar las excepciones, incluso antes de llegar a este documento hace algún tiempo.
Por lo general, las excepciones reducen el flujo de algunas operaciones y se manejan para convertirlas en errores comprensibles para los humanos. Por lo tanto, parece que una excepción en realidad es un mejor paradigma para manejar casos de error y trabajar en ellos para evitar un bloqueo completo de la aplicación / servicio y notificar al usuario / consumidor que algo salió mal.
Más respuestas sobre las preocupaciones de @thewhiteambit
Si su aplicación puede funcionar sin conexión sin datos persistentes en la base de datos, no debe usar excepciones , ya que la implementación del flujo de control
try/catch
se considera un antipatrón. El trabajo fuera de línea es un posible caso de uso, por lo que implementa el flujo de control para verificar si la base de datos es accesible o no, no espere hasta que sea inaccesible .El análisis también es un caso esperado ( no un CASO EXCEPCIONAL ). Si espera esto, ¡no use excepciones para controlar el flujo! . ¡Obtiene algunos metadatos del usuario para saber cuál es su cultura y utiliza formateadores para esto! .NET también admite este y otros entornos, y una excepción porque se debe evitar el formateo de números si espera un uso específico de su aplicación / servicio .
Este artículo es solo una opinión o un punto de vista del autor.
Dado que Wikipedia también puede ser solo la opinión del autor o los autores de la articulación, no diría que es el dogma , pero compruebe lo que dice el artículo de Codificación por excepción en algún párrafo:
También dice en alguna parte:
Uso de excepción incorrecto
Honestamente, creo que el software no se puede desarrollar, no se tome en serio los casos de uso. Si sabes eso ...
... no usarás excepciones para eso . Se podría apoyar estos casos de uso que utilizan flujo de control regular.
Y si no se cubre algún caso de uso inesperado, su código fallará rápidamente, porque arrojará una excepción . Correcto, porque una excepción es un caso excepcional .
Por otro lado, y finalmente, a veces cubre casos excepcionales arrojando excepciones esperadas , pero no las arroja para implementar el flujo de control. Lo hace porque desea notificar a las capas superiores que no admite algún caso de uso o que su código no funciona con algunos argumentos o datos / propiedades del entorno.
fuente
La única vez que debe preocupar a sus usuarios por algo que sucedió en el código es si hay algo que puedan o necesiten hacer para evitar el problema. Si pueden cambiar los datos en un formulario, presione un botón o cambie la configuración de una aplicación para evitar el problema y luego infórmeles. Pero las advertencias o errores que el usuario no tiene la capacidad de evitar solo los hace perder la confianza en su producto.
Las excepciones y los registros son para usted, el desarrollador, no para su usuario final. Comprender lo correcto cuando se detecta cada excepción es mucho mejor que simplemente aplicar alguna regla de oro o confiar en una red de seguridad para toda la aplicación.
La codificación sin sentido es el ÚNICO tipo de codificación incorrecta. El hecho de que sienta que hay algo mejor que se puede hacer en esas situaciones muestra que está involucrado en una buena codificación, pero evite tratar de sellar alguna regla genérica en estas situaciones y entienda la razón de que algo arroje en primer lugar y qué puedes hacer para recuperarte de ello.
fuente
Sé que esta es una pregunta antigua, pero aquí nadie mencionó el artículo de MSDN, y fue el documento que realmente me lo aclaró, MSDN tiene un muy buen documento sobre esto, debe detectar excepciones cuando se cumplen las siguientes condiciones:
Sugeriría leer toda la sección " Manejo de excepciones y excepciones " y también las Mejores prácticas para excepciones .
fuente
El mejor enfoque es el segundo (el que especifica el tipo de excepción). La ventaja de esto es que sabe que este tipo de excepción puede ocurrir en su código. Está manejando este tipo de excepción y puede reanudar. Si se produce alguna otra excepción, eso significa que algo está mal y que lo ayudará a encontrar errores en su código. La aplicación finalmente se bloqueará, pero se dará cuenta de que hay algo que se perdió (error) que debe corregirse.
fuente
Con excepciones, intento lo siguiente:
Primero, atrapo tipos especiales de excepciones, como división por cero, operaciones de E / S, etc., y escribo código de acuerdo con eso. Por ejemplo, una división por cero, dependiendo de la procedencia de los valores, podría alertar al usuario (por ejemplo, una calculadora simple en que en un cálculo medio (no los argumentos) llega a una división por cero) o para tratar silenciosamente esa excepción, registrando y seguir procesando.
Luego trato de atrapar las excepciones restantes y registrarlas. Si es posible, permita la ejecución del código; de lo contrario, avise al usuario de que ocurrió un error y pídale que envíe un informe de error.
En código, algo como esto:
fuente
0
numerador de antemano, en lugar de atry-catch
. Además, ¿por qué tomar el genéricoException
aquí? Es mejor dejar que el error brote que lidiar con él aquí en todos los casos en que no lo espere.El segundo enfoque es bueno.
Si no desea mostrar el error y confundir al usuario de la aplicación mostrando una excepción de tiempo de ejecución (es decir, un error) que no está relacionado con ellos, simplemente registre el error y el equipo técnico puede buscar el problema y resolverlo.
Le recomiendo que elija el segundo enfoque para toda su aplicación.
fuente
catch
los bloques siempre deben llamarthrow
para que se active la excepción o devolver algo / mostrar algo que le dice al usuario que la acción ha fallado. Desea recibir la llamada de soporte cuando no pueden guardar lo que sea, no 6 meses después cuando intentan recuperarlo y no pueden encontrarlo.Dejar un bloque de captura en blanco es lo peor que puede hacer. Si hay un error, la mejor manera de manejarlo es:
fuente
Para mí, manejar la excepción puede verse como una regla de negocios. Obviamente, el primer enfoque es inaceptable. El segundo es mejor y podría ser 100% correcto si el contexto lo dice. Ahora, por ejemplo, está desarrollando un complemento de Outlook. Si agrega una excepción no controlada, el usuario de Outlook ahora puede saberlo ya que el Outlook no se destruirá a sí mismo debido a que un complemento falló. Y tienes dificultades para descubrir qué salió mal. Por lo tanto, el segundo enfoque en este caso, para mí, es correcto. Además de registrar la excepción, puede decidir mostrar un mensaje de error al usuario; lo considero como una regla comercial.
fuente
La mejor práctica es lanzar una excepción cuando se produce el error. Porque se ha producido un error y no debe ocultarse.
Pero en la vida real puedes tener varias situaciones cuando quieres ocultar esto
fuente
Exception
. Nunca. Lanza una subclase apropiada deException
todo lo que quieras, pero nuncaException
porque eso no proporciona absolutamente ninguna información semántica. De plano no puedo ver un escenario en el que tenga sentido lanzar,Exception
pero no una subclase del mismo.Debe considerar estas Pautas de diseño para excepciones
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions
fuente
El
catch
sin ningún argumento es simplemente comer la excepción y no sirve de nada. ¿Qué pasa si ocurre un error fatal? No hay forma de saber qué sucedió si usas catch sin argumento.Una instrucción catch debería capturar excepciones más específicas como
FileNotFoundException
y luego, al final , debería capturarException
cuál capturaría cualquier otra excepción y las registraría.fuente
catch(Exception)
al final? Si no lo espera, siempre es una buena práctica pasarlo a la siguiente capa.A veces es necesario tratar las excepciones que no dicen nada a los usuarios.
Mi camino es:
Definitivamente no tiene que ser la mejor práctica. ;-)
fuente
Puedo decirte algo:
El fragmento n. ° 1 no es aceptable porque ignora la excepción. (Se lo traga como si nada hubiera pasado).
Por lo tanto, no agregue bloque de captura que no haga nada o simplemente vuelva a lanzar.
El bloque de captura debería agregar algún valor. Por ejemplo, mensaje de salida al usuario final o error de registro.
No utilice excepciones para la lógica del programa de flujo normal. Por ejemplo:
por ejemplo, validación de entrada. <- Esta no es una situación excepcional válida, más bien debe escribir un método
IsValid(myInput);
para verificar si el elemento de entrada es válido o no.Código de diseño para evitar excepciones. Por ejemplo:
Si pasamos un valor que no se puede analizar a int, este método arrojaría una excepción, en lugar de eso podríamos escribir algo como esto:
bool TryParse(string input,out int result);
<- este método devolvería un valor booleano que indica si el análisis fue exitoso.Tal vez esto esté un poco fuera del alcance de esta pregunta, pero espero que esto te ayude a tomar decisiones correctas cuando se trata de
try {} catch(){}
excepciones.fuente