Normalmente estoy de acuerdo con la mayoría de las advertencias de análisis de código, y trato de cumplirlas. Sin embargo, estoy teniendo más dificultades con este:
CA1031: no capturar tipos de excepción general
Entiendo la razón de esta regla. Pero, en la práctica, si quiero tomar la misma acción independientemente de la excepción lanzada, ¿por qué manejaría cada una específicamente? Además, si manejo excepciones específicas, ¿qué sucede si el código al que llamo cambia para lanzar una nueva excepción en el futuro? Ahora tengo que cambiar mi código para manejar esa nueva excepción. Mientras que si simplemente capturo Exception
mi código no tiene que cambiar.
Por ejemplo, si Foo llama a Bar, y Foo necesita detener el procesamiento, independientemente del tipo de excepción lanzada por Bar, ¿hay alguna ventaja en ser específico sobre el tipo de excepción que estoy captando?
Quizás un mejor ejemplo:
public void Foo()
{
// Some logic here.
LogUtility.Log("some message");
}
public static void Log()
{
try
{
// Actual logging here.
}
catch (Exception ex)
{
// Eat it. Logging failures shouldn't stop us from processing.
}
}
Si no detecta una excepción general aquí, entonces debe detectar todo tipo de excepción posible. Patrick tiene un buen punto que OutOfMemoryException
no debería tratarse de esta manera. Entonces, ¿qué pasa si quiero ignorar todas las excepciones pero OutOfMemoryException
?
fuente
OutOfMemoryException
? ¿El mismo código de manejo que todo lo demás?OutOfMemoryError
, que está separado delException
árbol de herencia por esa misma razónRespuestas:
Estas reglas son generalmente una buena idea y, por lo tanto, deben seguirse.
Pero recuerde que estas son reglas genéricas. No cubren todas las situaciones. Cubren las situaciones más comunes. Si tiene una situación específica y puede argumentar que su técnica es mejor (y debería poder escribir un comentario en el código para articular su argumento para hacerlo), hágalo (y luego haga que lo revisen por pares).
En el lado opuesto de la discusión.
No veo su ejemplo anterior como una buena situación para hacerlo. Si el sistema de registro está fallando (presumiblemente registrando alguna otra excepción), entonces probablemente no quiera que la aplicación continúe. Salga e imprima la excepción a la salida para que el usuario pueda ver lo que sucedió.
fuente
Sí, atrapar excepciones generales es algo malo. Una excepción generalmente significa que el programa no puede hacer lo que usted le pidió.
Hay algunos tipos de excepciones que puede manejar:
Ah, y como regla general: si no sabe qué hacer con una excepción si la detecta, es mejor simplemente fallar rápidamente (pasar la excepción a la persona que llama y dejar que se ocupe)
fuente
El bucle externo superior debe tener uno de estos para imprimir todo lo que pueda, y luego morir de una muerte horrible, violenta y ruidosa (ya que esto no debería suceder y alguien necesita escuchar).
De lo contrario, generalmente debe tener mucho cuidado, ya que es muy probable que no haya anticipado todo lo que podría suceder en este lugar y, por lo tanto, lo más probable es que no lo trate correctamente. Sea lo más específico posible para que solo atrape a los que sabe que sucederán, y deje que los que no se hayan visto antes lleguen a la muerte ruidosa mencionada anteriormente.
fuente
No es que sea malo, es solo que las capturas específicas son mejores. Cuando eres específico, significa que realmente entiendes, más concretamente, lo que está haciendo tu aplicación y tienes más control sobre ella. En general, si te encuentras con una situación en la que solo captas una excepción, la registras y continúas, probablemente hay algunas cosas malas que están sucediendo de todos modos. Si está captando específicamente las excepciones que sabe que puede arrojar un bloque de código o método, entonces hay una mayor probabilidad de que realmente pueda recuperarse en lugar de simplemente iniciar sesión y esperar lo mejor.
fuente
Las dos posibilidades no son mutuamente excluyentes.
En una situación ideal, detectaría todos los tipos posibles de excepción que podría generar su método, los manejaría por excepción y, al final, agregaría una
catch
cláusula general para detectar cualquier excepción futura o desconocida. De esta manera obtienes lo mejor de ambos mundos.Tenga en cuenta que para detectar las excepciones más específicas, debe ponerlas primero.
Editar: por las razones indicadas en los comentarios, se agregó una nueva versión en la última captura
fuente
He estado reflexionando sobre lo mismo recientemente, y mi conclusión tentativa es que la mera pregunta surge porque la jerarquía de excepciones .NET está gravemente desordenada.
Tomemos, por ejemplo, el humilde
ArgumentNullException
que podría ser un candidato razonable para una excepción que no desea detectar, porque tiende a indicar un error en el código en lugar de un error legítimo de tiempo de ejecución. Ah, sí. También lo haceNullReferenceException
, excepto queNullReferenceException
se derivaSystemException
directamente, por lo que no hay un depósito en el que pueda colocar todos los "errores lógicos" para atrapar (o no atrapar).Luego está el problema principal de IMNSHO de haberCuando obtienes unSEHException
derivado (víaExternalException
)SystemException
y, por lo tanto, hacerlo "normal".SystemException
SEHException
, quieres escribir un volcado y terminar lo más rápido posible, y comenzando con .NET 4 al menos algunos Las excepciones SEH se consideran excepciones estatales corruptas que no se detectarán. Una cosa buena, y un hecho que hace que laCA1031
regla sea aún más inútil, porque ahora tu perezosocatch (Exception)
no atrapará estos peores tipos de todos modos.Entonces, parece que las otras cosas de Framework derivan de manera bastante inconsistente, ya sea
Exception
directamente o por medioSystemException
, haciendo cualquier intento de agrupar las cláusulas de captura por algo como discutible de gravedad.Hay una pieza de
C#
fama del Sr. Lippert , llamada Vexing Exception , donde expone una categorización útil de las excepciones: se podría argumentar que solo quiere atrapar las "exógenas", excepto ...C#
el lenguaje y el diseño de .NET Las excepciones marco hacen imposible "atrapar solo a los exógenos" de manera sucinta. (y, por ejemplo, unaOutOfMemoryException
puede muy bien ser un error recuperable totalmente normal para una API que tiene que asignar memorias intermedias que son de alguna manera grande)En resumen, para mí es que la forma en que
C#
funcionan los bloques catch y la forma en que se diseña la jerarquía de excepciones de Framework, la reglaCA1031
es más que totalmente inútil . Se pretende que ayuda con la raíz del problema de "no tragar excepciones", pero tragar excepciones tiene cero que ver con lo que captura, pero con lo que se hace:Hay al menos 4 formas de manejar legítimamente un atrapado
Exception
, yCA1031
solo parece manejar superficialmente una de ellas (es decir, el caso de relanzamiento).Como nota al margen, hay una función C # 6 llamada Filtros de excepción que volverá a ser un
CA1031
poco más válida desde entonces, podrá filtrar adecuadamente, correctamente, las excepciones que desea capturar, y hay menos razones para escribir un filtro sin filtrarcatch (Exception)
.fuente
OutOfMemoryException
es que no hay una buena manera de que el código pueda estar seguro de que "solo" indica el fallo de una asignación particular que uno estaba preparado para fallar. Es posible que se haya lanzado alguna otra excepción más grave, y se hayaOutOfMemoryException
producido durante el desenrollado de la pila de esa otra excepción. Puede que Java haya llegado tarde a la fiesta con su "prueba con recursos", pero maneja las excepciones durante el desenrollado de la pila un poco mejor que .NET.finally
bloques contra excepciones que uno puede esperar de manera realista que ocurran de tal manera que las excepciones originales y nuevas se registren. Desafortunadamente, hacer eso a menudo requerirá la asignación de memoria, lo que podría causar fallas propias.El manejo de excepciones de Pokémon (¡tengo que atraparlos a todos!) Ciertamente no siempre es malo. Cuando expones un método a un cliente, especialmente a un usuario final, a menudo es mejor atrapar cualquier cosa y todo en lugar de que tu aplicación se bloquee y se queme.
En general, deben evitarse donde pueda. A menos que pueda tomar medidas específicas según el tipo de excepción, es mejor no manejarla y permitir que la excepción brote en lugar de tragarse la excepción o manejarla incorrectamente.
Echa un vistazo a esta respuesta SO para leer más.
fuente
LoadDocument()
, es esencialmente imposible identificar todas las cosas que podrían salir mal, pero el 99% de las excepciones que podrían lanzarse simplemente significarán "No fue posible interpretar el contenido de un archivo con el nombre dado como un documento; trátelo ". Si alguien intenta abrir algo que no es un archivo de documento válido, eso no debería bloquear la aplicación y eliminar otros documentos abiertos. El manejo de errores de Pokémon en estos casos es feo, pero no conozco ninguna buena alternativa.Capturar excepciones generales es malo porque deja su programa en un estado indefinido. No sabes dónde salió mal, así que no sabes qué ha hecho o qué no ha hecho realmente tu programa.
Donde permitiría atrapar todo es al cerrar un programa. Siempre y cuando puedas limpiarlo bien. Nada tan molesto como un programa que cierra, que solo arroja un cuadro de diálogo de error que no hace nada más que sentarse allí, no desaparecer y evitar que su computadora se cierre.
En un entorno distribuido, su método de registro podría ser contraproducente: detectar una excepción general podría significar que su programa aún mantiene un bloqueo en el archivo de registro evitando que otros usuarios realicen registros.
fuente
Como han dicho otros, es realmente difícil (si no imposible) imaginar alguna acción que quieras tomar independientemente de la excepción lanzada. Solo un ejemplo son situaciones en las que el estado del programa se ha dañado y cualquier procesamiento posterior puede generar problemas (esta es la razón subyacente
Environment.FailFast
).Para el código de pasatiempo está bien captarlo
Exception
, pero para el código de nivel profesional, la introducción de un nuevo tipo de excepción debe tratarse con el mismo respeto que un cambio en la firma del método, es decir, considerarse un cambio de última hora. Si se suscribe a este punto de vista, es inmediatamente obvio que volver a cambiar (o verificar) el código del cliente es el único curso de acción correcto.Claro, porque no capturará solo las excepciones lanzadas por
Bar
. También habrá excepciones que los clientes de Bar o incluso el tiempo de ejecución podrían lanzar durante el tiempo queBar
está en la pila de llamadas. Un bien escritoBar
debe definir su propio tipo de excepción si es necesario para que las personas que llaman puedan capturar específicamente las excepciones emitidas por sí mismo.En mi humilde opinión, esta es la forma incorrecta de pensar sobre el manejo de excepciones. Debería estar operando en listas blancas (capturar los tipos de excepción A y B), no en las listas negras (capturar todas las excepciones excepto X).
fuente
Tal vez . Hay excepciones para cada regla, y ninguna regla debe seguirse sin crítica. Su ejemplo podría ser uno de los casos en los que tiene sentido tragarse todas las excepciones. Por ejemplo, si desea agregar el rastreo a un sistema de producción crítico y quiere asegurarse absolutamente de que los cambios no interrumpan la tarea principal de la aplicación.
Sin embargo , debe pensar cuidadosamente sobre las posibles razones del fracaso antes de decidir ignorarlas en silencio. Por ejemplo, ¿qué pasa si la razón de la excepción es:
¿No desea que se le notifique de inmediato que este problema está presente para que pueda solucionarlo? Tragar la excepción significa que nunca se sabe que algo salió mal.
Algunos problemas (como que el disco está lleno) también pueden hacer que otras partes de la aplicación fallen, pero esta falla no se registra ahora, ¡así que nunca se sabe!
fuente
Me gustaría abordar esto desde una perspectiva lógica en lugar de una técnica,
Bueno, alguien tendría que manejarlo. Esa es la idea. Los escritores de código de biblioteca serían prudentes al agregar nuevos tipos de excepción, ya que es probable que rompa clientes, no debería encontrarse con esto muy a menudo.
Su pregunta es básicamente "¿Qué pasa si no me importa lo que salió mal? ¿Realmente tengo que pasar por la molestia de descubrir qué fue?"
Aquí está la parte de belleza: no, no lo haces.
"Entonces, ¿puedo mirar hacia otro lado y hacer que cualquier cosa desagradable aparezca automáticamente debajo de la alfombra y termine con eso?"
No, tampoco es así como funciona.
El punto es que la colección de posibles excepciones siempre es mayor que la colección que espera y está interesado en el contexto de su pequeño problema local. Manejas a aquellos que anticipas y si sucede algo inesperado, lo dejas a los manejadores de nivel superior en lugar de tragárselo. Si no te importa una excepción que no esperabas, apostaste a alguien en la pila de llamadas y sería un sabotaje matar una excepción antes de que llegue a su controlador.
"Pero ... pero ... ¡entonces una de esas otras excepciones que no me importan podría hacer que mi tarea falle!"
Si. Pero siempre serán más importantes que los manejados localmente. Como una alarma de incendio o el jefe diciéndole que deje de hacer lo que está haciendo y retome una tarea más urgente que apareció.
fuente
Debe detectar excepciones generales en el nivel superior de cada proceso y manejarlo informando el error lo mejor posible y luego finalizando el proceso.
No debe atrapar excepciones generales e intentar continuar la ejecución.
fuente