2009-11-21 19 views
14

Considere este método (perdón por el triste intento de Chuck Norris humor :)):Excepción ReSharper volver a lanzar posiblemente destinado

public class ChuckNorrisException : Exception 
{ 
    public ChuckNorrisException() 
    { 
    } 

    public ChuckNorrisException(string message) 
     : base(message) 
    { 
    } 

    public ChuckNorrisException(string message, Exception cause) 
     : base(message, cause) 
    { 
    } 

    protected ChuckNorrisException(SerializationInfo info, StreamingContext context) 
     : base(info, context) 
    { 
    } 
} 

static void ExceptionTest(double x) 
{ 
    try 
    { 
     double y = 10/x; 
     Console.WriteLine("quotient = " + y); 
    } 
    catch (Exception e) 
    { 
     e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) : 
      e; 
     throw e; 
    } 
} 

En ReSharper, recibo una advertencia en la línea de "tirar e" diciendo "excepción posiblemente volver a lanzar destinado a". Pero, obviamente, en este caso esa no es la intención, ya que e podría incluirse en ChuckNorrisException, y si solo uso "throw", esa excepción no se lanzaría.

Sé que puedo suprimir la advertencia de reafilar, pero luego se apagará para todos los escenarios si no me equivoco. Solo me preguntaba si alguien más se había encontrado con esto. La única solución que he encontrado es hacer otra variable de excepción (e2, por ejemplo), y arrojar eso. Eso puede ser lo mejor que puedo hacer aquí. Sin embargo, parece que el reafilado podría detectar este problema y ser lo suficientemente inteligente como para saber que si se modifica e, entonces tirar e está bien.

Gracias.

[EDITAR] Lo siento, olvidé un paso. Antes de banda, tengo que registrar la excepción, así que no puedo acaba de hacer:

e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) : 
      e; 
throw e; 

que tengo que hacer:

e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) : 
      e; 
LogException(e); 
throw e; 
+0

¿Por qué tiene que registrar la excepción antes de lanzarla (incluso si está envuelta en otra excepción)? ¿Por qué la persona que llama no puede registrar la excepción, incluida la excepción interna? –

+0

Considere si este fuera un método de servicio en un servicio REST, por ejemplo, y deseara registrar esa excepción en el archivo de registro del servidor. Entonces, la persona que llama (por ejemplo, el cliente web) * podría * registrar la excepción, pero sería mucho mejor tener acceso a esa información de excepción en el registro del servidor. – dcp

+0

Una excepción presentada en el código .NET en el servidor no puede ser capturada por un cliente. La excepción debe quedar atrapada en algún lugar del servidor; de lo contrario, el servidor se bloqueará. –

Respuesta

29

Tal vez no estoy entender la pregunta, por favor corregir si tengo el extremo equivocado.

Hay dos casos ocurre aquí:

  1. La primera es que se captura la excepción original. Luego envuelve en una nueva instancia de excepción como la excepción interna, luego tira el nuevo. No se pierde información en este caso (la excepción interna conserva toda la información), por lo que no se da ninguna advertencia.

  2. La segunda es que atrapa y vuelve a lanzar la excepción original. Si vuelve a tirar, usted nunca debe usar "lanzar e", ya que alterará el rastro de la pila. Es por eso que ReSharper está imprimiendo una advertencia . Para volver a lanzar la excepción detectada, debe usar la palabra clave "throw" por sí misma.

La respuesta a this question lo explica mejor que yo. Debido a los efectos secundarios sutiles y al gran número de personas que obtienen este detalle incorrecto, yo personalmente veo la sintaxis de re-lanzamiento como defectuosa.

De todos modos esa es una descripción de por qué está recibiendo una advertencia. Esto es lo que haría en lugar de ello:

catch(DivideByZeroException e) 
{ 
    // we don't catch any other exceptions because we weren't 
    // logging them or doing anything with the exception before re-throwing 
    throw new ChuckNorrisException("Useful information", e); 
} 

* Editar - Si tienes que entrar excepciones, sólo puede hacer algo como esto en su lugar.Nota: Esta es mi solución preferida ya que creo que lee mejor y es menos probable que contenga un error de la consulta para este tipo de excepción a sí mismo:

// catch most specific exception type first 
catch(DivideByZeroException e) 
{ 
    Log(e); 
    throw new ChuckNorrisException("Useful information", e); 
} 
catch(Exception e) // log & re-throw all other exceptions 
{ 
    Log(e); 
    throw; // notice this is *not* "throw e"; this preserves the stack trace 
} 

Otra alternativa sería:

catch(Exception e) 
{ 
    Log(e); 
    if(e is DivideByZeroException) 
    { 
     // wrap + throw the exception with our new exception type 
     throw new ChuckNorrisException("useful info", e); 
    } 

    // re-throw the original, preserving the stack trace 
    throw; 
} 
+0

Gracias por la respuesta, pero dejé un paso en mi pregunta original (tengo que registrar el excepción). Lo siento por eso. – dcp

+1

¡Gran respuesta! No sabía eso de "lanzar e" jugando con el rastro de la pila. Por cierto, en caso de que alguien más esté interesado aquí hay un buen enlace sobre este tema en caso de que quiera obtener más información: http://www.tkachenko.com/blog/archives/000352.html Una cosa que no hice Tampoco sé si este comportamiento es diferente en C# frente a JAVA. Gracias de nuevo Mark! – dcp

+0

Me complace ayudar :) –

2

Esto tendrá el mismo efecto que el código que ha publicado y no debe causar la advertencia.

catch (DivideByZeroException de) 
    { 
     throw new ChuckNorrisException("Only Chuck Norris can divide by 0!", de); 
    } 
} 
+0

Tienes razón, sin embargo, dejé un paso en mi pregunta original (mi culpa). Lo siento por eso. – dcp

Cuestiones relacionadas