2010-10-20 10 views
8

¿Por qué es preferible lanzar esta excepción¿Cuál es el propósito de lanzar subclases de excepciones específicas?

Throw New DivideByZeroException("You can't divide by zero") 

sobre ese general uno:

Throw New Exception("You can't divide by zero") 

¿Qué ventaja que se gana en este ejemplo en particular? El mensaje ya lo dice todo. ¿Alguna vez las subclases estándar que heredan de la clase Exception base tienen diferentes métodos que la base? No he visto un caso, pero debo admitir que tiendo a arrojar la excepción base.

+0

Como puede detectar excepciones específicas, puede tener varias declaraciones catch en un try ... catch block. – Nate

+0

"¿Las subclases estándar que heredan de la clase de excepción base tienen métodos diferentes a los de la base?": Sí, pero la mayoría de las respuestas no se enfocaron en este punto. Consulte 'WebException' para obtener un ejemplo simple de esto: http://msdn.microsoft.com/en-us/library/system.net.webexception.aspx. Específicamente, tome nota de la propiedad 'Response'. – Brian

Respuesta

20

El tipo de excepción permite a los controladores de la excepción filtrarlo. Si todo lo que lanzaste fueran excepciones del tipo Exception, ¿cómo sabrían los manejadores qué excepciones capturar y cuáles para permitir la propagación de la pila de llamadas?

Por ejemplo, si siempre tirar Exception:

void Foo(string item) { 
    try { 
    if (Bar(item)) { 
     Console.WriteLine("BAR!"); 
    } 
    } catch (Exception e) { 
    Console.WriteLine("Something bad?"); 
    } 
} 

bool Bar(string item) { 
    if (item == null) { 
    throw new Exception("Argument is null!"); 
    } 

    return Int32.Parse(item) != 0; 
} 

¿Cómo la persona que llama Foo saben si se ha producido una excepción nula o si el Int32.Parse() fallidos? Tiene que verificar el tipo de la excepción lanzada (o hacer alguna comparación desagradable de cadenas).

Es aún más preocupante si se obtiene un ThreadAbortException o OutOfMemoryException que puede ocurrir en lugares que no se puede esperar una excepción. En estos casos, si su código de captura solo captura Exception, puede enmascarar estas excepciones (importantes) y causar daños al estado de su programa (o sistema).

El código de ejemplo debe decir:

void Foo(string item) { 
    try { 
    if (Bar(item)) { 
     Console.WriteLine("BAR!"); 
    } 
    } catch (ArgumentNullException ae) { 
    Console.WriteLine("Null strings cannot be passed!"); 
    } catch (FormatException fe) { 
    Console.WriteLine("Please enter a valid integer!"); 
    } 
} 

bool Bar(string item) { 
    if (item == null) { 
    throw new ArgumentNullException("item"); 
    } 

    return Int32.Parse(item) != 0; 
} 
+0

+1, nice one ... –

+0

Para acompañar esta respuesta: el parámetro Message debe ser una cadena legible por humanos. Claro que podrías analizarlo cuando captures una excepción genérica, pero eso es algo que no me gusta. Especialmente si el mensaje puede cambiar. – Allan

7

Debido a que puede tener múltiples declaraciones de capturas y controlar errores diferentes de manera diferente.

Por ejemplo, una excepción DivideByZero podría solicitar al usuario que corrija una entrada, mientras que una excepción FileNotFound podría alertar a un usuario de que el programa no puede continuar y cerrar el programa.

Hay un buen artículo en profundidad responder a esta pregunta aquí: http://blogs.msdn.com/b/dotnet/archive/2009/02/19/why-catch-exception-empty-catch-is-bad.aspx

5

En lugar de filtrado basado en el texto enviar a lo largo del flujo de error, se puede tomar varios tipos de excepciones. Cada uno puede tener una forma muy específica de realizar una recuperación. El texto está allí para proporcionar al usuario o depurador algunos comentarios, pero el programa se preocupa por el tipo de excepción. Por la misma razón hay polimorfismo para las clases creadas por el usuario, hay excepciones.

Es mucho más fácil incluir varias declaraciones de captura para diferentes tipos de excepción que analizar el texto del mensaje para comprender qué se debe hacer para solucionarlo correctamente.

+0

además de analizar el texto del mensaje cuando su localización se vuelve casi imposible –

3

Las diversas subclases de Exception tienen un significado semántico: una ArgumentNullException indica un problema diferente al que genera una excepción DivideByZeroException, y el programador puede manejar estos problemas de forma diferente. Además, las subclases pueden definir propiedades o métodos adicionales que pueden ayudar a diagnosticar o manejar el problema, si el programador elige usarlos.

1
try { 
    Do(); 
} 
catch (MyException) 
{ 
    // reaction on MyException 
} 
catch (AnotherException) 
{ 
    // another reaction on AnotherException 
{ 
// SomeException will not be caught 


void Do() 
{ 
    if (...) 
     throw new MyException(); 
    else if (...) 
     throw new AnotherException(); 
    else 
     throw new SomeException(); 
} 
1

En una jerarquía de excepciones bien diseñada, tener diferentes tipos de excepciones permite obtener declaraciones catch que toman diferentes acciones en diferentes circunstancias. Idealmente, una familia de excepciones surgiría de una función si la acción particular no pudiera completarse, pero las invariantes de clase que deberían haber sido aplicables antes de que la acción se intentara probablemente aún se mantengan, con la única excepción de aquellas implícitas en la falla de la acción. Por ejemplo, el método get-object de una colección debe lanzar una excepción de esta familia si la colección parece válida pero el objeto solicitado no existe.

Si una función falla de tal manera que indica que las invariantes de clase no se sostuvieron cuando se llamó, o ya no se retienen después de que regresa, debe lanzar una excepción de una familia diferente. Tenga en cuenta que puede ser apropiado para una función capturar una excepción de la primera familia y volver a lanzarla como la segunda, si la única forma en que se hubiera producido la excepción sería si se infringen las invariantes. También puede ser apropiado en ocasiones capturar una excepción del segundo tipo y arrojar uno de los primeros, si las excepciones provienen de un objeto que nunca va a ser utilizado después de que la función retorne.

Si las cosas son realmente grave (por ejemplo OutOfMemoryException, CpuCatchingFireException, etc.) que debe ser separada otra jerarquía de los dos primeros.

excepciones existentes no siguen este patrón, pero se podría utilizar para cualquier nuevo excepciones uno crea.

4

Directamente desde MSDN - Exception Handling:

Considere la captura de excepciones específicas cuando se comprende por qué va a ser lanzado en un contexto dado.

Debe coger sólo aquellas excepciones que puede recuperarse. Por ejemplo, un FileNotFoundException que resulta de un intento de abrir un archivo no existente puede ser manejado por una aplicación, ya que puede comunicar el problema al usuario y permitir al usuario especificar un nombre de archivo diferente o crear el archivo. Una solicitud para abrir un archivo que genera una ExecutionEngineException no debe ser manejado porque la causa subyacente de la excepción no puede ser conocido con certeza, y la aplicación no puede asegurar que es seguro continuar con la ejecución.

No abuse de catch, como lanzar otra excepción dentro de un bloque catch se restablecerá el seguimiento de la pila y provocar la pérdida de información de depuración importante, ya que una vez más MSDN sugiere:

No abuse captura. Las excepciones a menudo deberían permitirse propagarse en la pila de llamadas.

excepciones Catching que no se puede manejar legítimamente cueros información crítica depuración.

Al final, la captura de una excepción debe ser para el manejo de excepciones específicas que espera que se produzca en determinadas escenario común en la que desea iniciar la sesión o tener algún comportamiento específico sobre la captura excepción, de lo contrario simplemente throw a la basura, como Eric Lippert mismo recomienda en su blog (ver Too much reuse artículo).

try { 
    ... 
} catch (Exception ex) { 
    throw; // This does not reset the stack trace. 
} 

En lugar de:

try { 
    ... 
} catch (Exception ex) { 
    throw ex; // This does reset the stack trace. 
} 

Por último, un Exception hace necesidad no es obligatorio ofrecer algunas características específicas como las propiedades o métodos complementarios o de ningún tipo, es el nombre de la misma que habla por sí mismo, lo que permite usted para filtrar su captura sobre un tipo específico de excepción.

editar # 1

Otro enlace interesante acerca de la manipulación en el blog de Eric Lippert error: Vexing exceptions.

3

Una excepción suele ser (1) atrapada, registrada y relanzada, (2) atrapada y manipulada, o (3) no capturada.

Si se detecta, se registra y se vuelve a lanzar, podría haber información adicional importante escondida en un tipo de excepción específica que permite que el código de registro arroje información más rica, de modo que el analista que intenta depurar el problema que causó la excepción puede hacerlo de manera más eficiente.

Si se detecta y maneja, entonces necesita saber cómo manejar el problema. Si una excepción es de un tipo específico, esa es una gran pista para el desarrollador que está escribiendo el controlador sobre si pueden manejar la excepción o no. Solo debe manejar las excepciones que espera y saber cómo recuperarse.

Si no se detecta, el proceso se reducirá y probablemente se envíe un volcado de emergencia a algún lugar. Ahora estamos de vuelta en caso (1).

En los tres casos, tener más información del tipo es preferible a tener menos.

Cuestiones relacionadas