¿Cuáles son las mejores prácticas a tener en cuenta al detectar excepciones y volver a lanzarlas? Quiero asegurarme de que se conservan el rastro del Exception
objeto InnerException
y la pila. ¿Hay alguna diferencia entre los siguientes bloques de código en la forma en que manejan esto?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
Vs:
try
{
//some code
}
catch
{
throw;
}
fuente
ExceptionDispatchInfo.Capture(ex).Throw(); throw;
en .NET +4.5 stackoverflow.com/questions/57383/…Si lanza una nueva excepción con la excepción inicial, también conservará el seguimiento inicial de la pila.
fuente
AggregateException
solo debe usarse para excepciones sobre operaciones agregadas. Por ejemplo, es arrojado por las clasesParallelEnumerable
yTask
del CLR. El uso probablemente debería seguir este ejemplo.En realidad, hay algunas situaciones en las que la
throw
declaración no preservará la información de StackTrace. Por ejemplo, en el siguiente código:StackTrace indicará que la línea 54 provocó la excepción, aunque se planteó en la línea 47.
En situaciones como la descrita anteriormente, hay dos opciones para preseleccionar el StackTrace original:
Llamar a Exception.InternalPreserveStackTrace
Como es un método privado, debe invocarse utilizando la reflexión:
Tengo la desventaja de depender de un método privado para preservar la información de StackTrace. Se puede cambiar en futuras versiones 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 In C #, ¿cómo puedo volver a lanzar InnerException sin perder la pregunta de seguimiento de la pila ?
Aunque tiene la ventaja de depender de métodos públicos, también depende del siguiente constructor de excepciones (que algunas excepciones desarrolladas por terceros no implementan):
En mi situación, tuve que elegir el primer enfoque, porque las excepciones planteadas por una biblioteca de terceros que estaba usando no implementaron este constructor.
fuente
Cuando usted
throw ex
, esencialmente está lanzando una nueva excepción, y perderá la información de seguimiento de la pila original.throw
Es el método preferido.fuente
La regla general es evitar atrapar y arrojar el
Exception
objeto básico . Esto te obliga a ser un poco más inteligente sobre las excepciones; en otras palabras, debe tener una captura explícita para aSqlException
para que su código de manejo no haga algo mal con aNullReferenceException
.Sin embargo, en el mundo real, la captura y el registro de la excepción base también es una buena práctica, pero no se olvide de caminar todo para obtener
InnerExceptions
lo que pueda tener.fuente
Siempre debes usar "tirar"; para volver a lanzar las excepciones en .NET,
Consulte esto, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
Básicamente, MSIL (CIL) tiene dos instrucciones: "lanzar" y "volver a lanzar":
Básicamente puedo ver la razón por la cual "throw ex" anula el seguimiento de la pila.
fuente
throw ex;
volverá a lanzar, ¡en Java, lo hace! Pero debe incluir esa información aquí para tener una respuesta de Grado A. (Aunque todavía estoy poniéndome al día con laExceptionDispatchInfo.Capture
respuesta de jeuoekdcwzfwccu .)Nadie ha explicado la diferencia entre
ExceptionDispatchInfo.Capture( ex ).Throw()
y una simplethrow
, así que aquí está. Sin embargo, algunas personas han notado el problema conthrow
.La forma completa de volver a generar una excepción detectada es utilizarla
ExceptionDispatchInfo.Capture( ex ).Throw()
(solo disponible en .Net 4.5).A continuación están los casos necesarios para probar esto:
1)
2)
3)
4)
El caso 1 y el caso 2 le darán un seguimiento de la pila donde el número de línea del código fuente para el
CallingMethod
método es el número dethrow new Exception( "TEST" )
línea de la línea.Sin embargo, el caso 3 le dará un seguimiento de la pila donde el número de línea del código fuente para el
CallingMethod
método es el número de línea de lathrow
llamada. Esto significa que si lathrow new Exception( "TEST" )
línea está rodeada por otras operaciones, no tiene idea de en qué número de línea se produjo la excepción.El caso 4 es similar con el caso 2 porque el número de línea de la excepción original se conserva, pero no es un cambio real porque cambia el tipo de la excepción original.
fuente
throw ex;
y esta es la mejor respuesta de todas.Algunas personas realmente perdieron un punto muy importante: 'throw' y 'throw ex' pueden hacer lo mismo, pero no le dan una pieza crucial de información, que es la línea donde ocurrió la excepción.
Considere el siguiente código:
Cuando haces un 'lanzamiento' o un 'lanzamiento ex', obtienes el seguimiento de la pila, pero la línea # será la # 22, por lo que no puedes descubrir qué línea exactamente estaba lanzando la excepción (a menos que solo tengas 1 o pocas líneas de código en el bloque try). Para obtener la línea esperada # 17 en su excepción, deberá lanzar una nueva excepción con el seguimiento original de la pila de excepciones.
fuente
También puedes usar:
Y todas las excepciones lanzadas subirán al siguiente nivel que las maneja.
fuente
Definitivamente usaría:
Eso preservará tu pila.
fuente
throw
; por ejemplo, podría limpiar un desechable (donde SOLO lo llama por error) y luego lanzar la excepción.FYI Acabo de probar esto y el seguimiento de la pila reportado por 'throw;' no es un seguimiento de pila completamente correcto. Ejemplo:
El seguimiento de la pila apunta al origen de la excepción correctamente (número de línea informado) pero el número de línea informado para foo () es la línea del lanzamiento; , por lo tanto, no puede decir cuál de las llamadas a bar () causó la excepción.
fuente