2009-09-11 26 views
5

Esta pregunta proviene de un análisis de código ejecutado contra un objeto que he creado. El análisis dice que debería detectar un tipo de excepción más específico que solo la Excepción básica.Captura de excepciones específicas frente a genéricas en C#

¿Se encuentra usando solo la excepción genérica o intentando atrapar una excepción específica y una excepción por defecto usando múltiples bloques catch?

Uno de los trozos de código en cuestión es el siguiente:

internal static bool ClearFlags(string connectionString, Guid ID) 
{ 
    bool returnValue = false; 
    SqlConnection dbEngine = new SqlConnection(connectionString); 
    SqlCommand dbCmd = new SqlCommand("ClearFlags", dbEngine); 
    SqlDataAdapter dataAdapter = new SqlDataAdapter(dbCmd); 

    dbCmd.CommandType = CommandType.StoredProcedure; 
    try 
    { 
     dbCmd.Parameters.AddWithValue("@ID", ID.ToString()); 

     dbEngine.Open(); 
     dbCmd.ExecuteNonQuery(); 
     dbEngine.Close(); 

     returnValue = true; 
    } 
    catch (Exception ex) 
    { ErrorHandler(ex); } 

    return returnValue; 
} 

Gracias por su consejo

EDIT: Aquí es la advertencia del análisis de código

Advertencia 351 CA1031: Microsoft.Design: modifique 'ClearFlags (cadena, Guid)' para detectar una excepción más específica que 'Exception' o vuelva a lanzar la excepción

Respuesta

22

Casi nunca debería atrapar la Excepción de nivel superior.

En la mayoría de los casos, debe detectar y manejar la excepción más específica posible y solo si hay algo útil que puede hacer con ella.

La excepción (jaja) a esto es si está atrapando para iniciar sesión y volver a lanzar la excepción, entonces a veces es correcto capturar una Excepción de nivel superior, registrarla y volver a lanzarla.

Casi nunca debe atrapar una Excepción de nivel superior y tragarla. Esto se debe a que si atrapa una excepción de nivel superior, realmente no sabe qué está manipulando; Absolutamente cualquier cosa podría haberlo causado, por lo que seguramente no podrá hacer nada que maneje correctamente cada caso de falla. Probablemente haya algunos fallos que quizás solo desee manejar y tragar de forma silenciosa, pero simplemente tragando Excepciones de nivel superior también se tragará un montón que realmente debería haber sido lanzado hacia arriba para que su código lo maneje más arriba. En el ejemplo del código, lo que probablemente quiera hacer es manejar una SQLException y registrar + tragar eso; y luego, para una excepción, registre y vuelva a lanzarlo. Esto te cubre a ti mismo. Todavía está registrando todos los tipos de excepción, pero solo se traga la SQLException bastante predecible que indica problemas con su SQL/base de datos.

Una práctica común es que para cada manejador solo se pueden resolver excepciones en ese punto, si no puede resolverlo en ese punto en el código, entonces permite que burbujee hacia arriba. Si no puede resolverlo en el siguiente nivel, permita que continúe. Si alcanza la parte superior no controlada, muestre una aplicación educada al usuario (quizás intente guardar automáticamente) y cierre la aplicación. En general, se considera peor permitir que una aplicación continúe ejecutándose después de una excepción no controlada porque no se puede predecir el estado de la aplicación como algo excepcional. Es mejor simplemente apagar y reiniciar la aplicación para volver a un estado esperado.

+0

@Simon: ¡Gracias! Aprecio la respuesta! ¿Puedes darme un poco más sobre por qué no debes atrapar y tragar una Excepción de nivel superior? La forma en que tengo mi código ahora, el método "ErrorHandler" reúne tanta información como puede de la excepción y la esconde en el registro de eventos (seguimiento de pila, excepción interna, mensaje, etc.). No estoy seguro de las consecuencias de capturar/procesar la excepción desde su punto de vista. –

+0

¿Cuál sería el objetivo de detectar una excepción, iniciar sesión y luego volver a lanzarla? –

+2

@Kyralessa: Baje la pila de códigos en el DAL. Puede que las cosas vayan mal y quiera iniciar sesión. Por lo tanto, puede capturar y registrar una Excepción de nivel superior, pero no debe tragarla porque realmente no sabe lo que salió mal (y te arriesgas a tragar algo serio como OutOfMemEx). Por lo tanto, debes volver a lanzarlo. Si quisieras manejarlo, deberías tomar el tipo de excepción específica que sabes cómo manejar. Si todo lo que puede recurrir es atrapar "Excepción", entonces debe volver a lanzar (o ajustar y volver a lanzar) para permitir que una persona que llama más arriba lo maneje apropiadamente. @Scott: He expandido mi respuesta un poco. –

1

Sí,

Debe coger desde la excepción más específica hasta lo más mínimo, para que pueda hacer frente a las cosas de una manera apropiada.

Por ejemplo, si realiza una solicitud web, primero debe detectar cosas como TimeOuts y 404, luego puede informar al usuario final que deben volver a intentarlo (tiempo de espera) y/o verificar la URL que ingresaron.

Entonces podrías ver algo menos general, en caso de que algo un poco más alocado salga mal, y luego volver a caer en una excepción en caso de que ocurra algo ridículo.

1

Como práctica recomendada, debe evitar detectar Excepción y usar indicadores como valores de retorno.

En su lugar, debe diseñar excepciones personalizadas para excepciones esperadas y detectarlas directamente. Cualquier otra cosa debería aparecer como una excepción inesperada.

En su ejemplo anterior, es posible que desee volver a lanzar una Excepción más específica de la empresa.

2

Debería leer un documento general o google "Manejo estructurado de excepciones" y obtener una mejor idea de lo que trata este tema, pero en general, atrapar todas las excepciones se considera una mala práctica porque no tiene idea de la excepción was (Error de memoria, error de memoria, falla de disco, etc.).

Y para muchas excepciones desconocidas/inesperadas, no debe permitir que la aplicación continúe. En general, usted "atrapa" y maneja únicamente las excepciones que Toy ha determinado, como resultado de un análisis del método para el que está codificando la cláusula catch, que ese método puede de hecho crear, y que puede hacer algo al respecto. La única vez que debe atrapar todas las expcetoins (capturar Exception x) es hacer algo como iniciar sesión, en cuyo caso debe volver a lanzar inmediatamente la misma excepción (lo que sea) para que pueda subir la pila a una excepción general "Excepción no controlada" Handler "que puede mostrar un mensaje apropiado al usuario y luego hacer que la aplicación finalice.

0

Estoy de acuerdo con la herramienta de análisis de código. Mi excepción a la regla es que atrapo la excepción general en mis controladores de eventos y al usuario se le da la opción de informar el error o ignorarlo.

En el ejemplo que proporciona, creo que el análisis del código lo hizo bien. Si no puedes manejar una excepción específica ahí mismo, no deberías atrapar nada y dejar que suba hasta el nivel más alto. De esta forma, será mucho más fácil volver a crear el problema cuando trates de solucionarlo.

Puede mejorar su ejemplo agregando la cadena de conexión y el valor de ID a la propiedad Data de la excepción y asegúrese de que también esté registrado. De esta forma, te das la oportunidad de luchar para reproducir el error.

5

Tener un vistazo a este artículo de Krzysztof Cwalina, que he encontrado muy útil para comprender cuándo coger o ignorar excepciones:

How to Design Exception Hierarchies

Todos los principios que describe sobre el diseño de las jerarquías de excepción son también aplicable cuando se decide cuándo atrapar, lanzar o ignorar las excepciones. Se divide en tres grupos excepciones:

  • errores de uso, como DivideByZeroException, que indican errores en el código; no debe manejar estos porque pueden evitarse cambiando su código.
  • Errores lógicos, como FileNotFoundException, que debe manipular porque no puede garantizar que no vayan a suceder. (Incluso si comprueba la existencia del archivo, aún podría eliminarse en esa fracción de segundo antes de leerlo).)
  • Fallas del sistema, como OutOfMemoryException, que no puede evitar ni manipular.
1

Estoy de acuerdo en que, en general, solo debe detectar las excepciones que espera y comprender cómo manejarlas. Unos pocos casos en los que a menudo no hacen esto:

  1. Como se mencionó anteriormente, si estoy capturando algún tipo de información útil para iniciar la sesión y luego volver a lanzar.

  2. Si estoy realizando una operación asincrónica, como manejar mensajes en cola o trabajos en un hilo de trabajo, y quiero ver la excepción para volver a lanzar en un contexto diferente. También utilizo a menudo un hack feo que engaña al CLR para que agregue información de seguimiento de la pila, de modo que no se pierda al volver a lanzar en el nuevo contexto.

  3. Si estoy trabajando con una tarea u operación aislada y puedo manejar la excepción al cerrar la tarea, sin cerrar toda la aplicación. A menudo deseo aquí que haya una excepción de nivel superior para excepciones realmente fatales (como OutOfMemoryException), ya que he estado ignorando esto. La forma correcta de manejar esto sería ejecutar la tarea aislada en su propio Dominio de aplicación, pero todavía no he tenido el tiempo de programación disponible para implementar esto en un proyecto.

Cuestiones relacionadas