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:
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 provocó la excepción, aunque se planteó 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, hay dos opciones para preseleccionar el StackTrace original:
Llamar a Exception.InternalPreserveStackTrace
Como es un método privado, debe invocarse utilizando la 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 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 ?
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 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 planteadas por una biblioteca de terceros que estaba usando no implementaron este constructor.