Mejores prácticas para detectar y volver a lanzar excepciones de .NET

Resuelto Seibar asked hace 16 años • 11 respuestas

¿Cuáles son las mejores prácticas a considerar al detectar excepciones y volver a lanzarlas? Quiero asegurarme de que se conserven el seguimiento del Exceptionobjeto InnerExceptiony de la pila. ¿Existe alguna diferencia entre los siguientes bloques de código en la forma en que manejan esto?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

Contra:

try
{
    //some code
}
catch
{
    throw;
}
Seibar avatar Aug 22 '08 22:08 Seibar
Aceptado

La forma de preservar el seguimiento de la pila es mediante el uso de throw;Esto también es válido

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex;Es básicamente como lanzar una excepción desde ese punto, por lo que el seguimiento de la pila solo irá al lugar donde se emite la throw ex;declaración.

Mike también tiene razón, suponiendo que la excepción le permita pasar una excepción (lo cual se recomienda).

Karl Seguin también tiene un excelente artículo sobre el manejo de excepciones en su libro electrónico Fundamentos de programación , que es una excelente lectura.

Editar: enlace de trabajo al pdf de Fundamentos de programación . Simplemente busque en el texto "excepción".

Darren Kopp avatar Aug 22 '2008 15:08 Darren Kopp

Si lanza una nueva excepción con la excepción inicial, también conservará el seguimiento de la pila inicial.

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}
Mike avatar Aug 22 '2008 15:08 Mike

En realidad, hay algunas situaciones en las que la throwdeclaración no conservará la información de StackTrace. Por ejemplo, en el siguiente código:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

StackTrace indicará que la línea 54 generó la excepción, aunque se generó en la línea 47.

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

En situaciones como la descrita anteriormente, existen dos opciones para conservar el StackTrace original:

Llamando a la excepción.InternalPreserveStackTrace

Como es un método privado, debe invocarse mediante reflexión:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

Tengo la desventaja de depender de un método privado para preservar la información de StackTrace. Se puede cambiar en versiones futuras de .NET Framework. El ejemplo de código anterior y la solución propuesta a continuación se extrajeron del blog de Fabrice MARGUERIE .

Llamando a Exception.SetObjectData

Anton Tykhyy sugirió la siguiente técnica como respuesta a la pregunta En C#, ¿cómo puedo volver a lanzar InnerException sin perder el seguimiento de la pila ?

static void PreserveStackTrace (Exception e) 
{ 
  var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ; 
  var mgr = new ObjectManager     (null, ctx) ; 
  var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ; 

  e.GetObjectData    (si, ctx)  ; 
  mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData 
  mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData 

  // voila, e is unmodified save for _remoteStackTraceString 
} 

Aunque tiene la ventaja de depender únicamente de métodos públicos, también depende del siguiente constructor de excepciones (que algunas excepciones desarrolladas por terceros no implementan):

protected Exception(
    SerializationInfo info,
    StreamingContext context
)

En mi situación, tuve que elegir el primer enfoque, porque las excepciones generadas por una biblioteca de terceros que estaba usando no implementaban este constructor.

CARLOS LOTH avatar Jul 01 '2012 19:07 CARLOS LOTH

Cuando lo haces throw ex, básicamente estás lanzando una nueva excepción y perderás la información de seguimiento de la pila original. throwes el método preferido.

Forgotten Semicolon avatar Aug 22 '2008 15:08 Forgotten Semicolon