c# - ¿Cómo puedo volver a emitir una excepción interna mientras mantengo el seguimiento de pila generado hasta ahora?
.net exception (8)
Aunque puede sentir que la excepción TargetInvocationException es "inútil", es la realidad. No intente fingir que .NET no tomó la excepción original y envuélvala con una excepción TargetInvocationException y tírela. Eso realmente sucedió. Algún día, es posible que incluso desee obtener parte de la información que proviene de esa envoltura, como quizás la ubicación del código que lanzó la excepción TargetInvocationException.
Duplicado de: En C #, ¿cómo puedo volver a generar InnerException sin perder el seguimiento de pila?
Tengo algunas operaciones que invoco de forma asíncrona en un hilo de fondo. A veces, las cosas van mal. Cuando esto sucede, tiendo a obtener una excepción TargetInvocationException que, si bien es apropiada, es bastante inútil. Lo que realmente necesito es la InnerException de TargetInvocationException, como esto:
try
{
ReturnValue = myFunctionCall.Invoke(Target, Parameters);
}
catch (TargetInvocationException err)
{
throw err.InnerException;
}
De esa manera, mis llamadores son atendidos con la excepción REAL que ocurrió. El problema es que la instrucción throw parece restablecer el seguimiento de pila. Básicamente, me gustaría volver a lanzar la excepción interna, pero mantener el rastro de pila que tenía originalmente. ¿Cómo puedo hacer eso?
ACLARACIÓN: La razón por la que quiero solo la excepción interna es que esta clase intenta "abstraer" todo el hecho de que estas funciones (delegados proporcionados por el llamante) se ejecutan en otros subprocesos y otras cosas. Si hay una excepción, es probable que no tenga nada que ver con que se ejecute en un subproceso en segundo plano, y la persona que llama realmente desea el seguimiento de la pila que va a su delegado y encuentra el problema real, no mi llamado a invocar.
Como han dicho otros, use la palabra clave "lanzar" sin agregarla para mantener intacta la cadena de excepción. Si necesita esa excepción original (suponiendo que es lo que quiere decir), puede llamar a Exception.GetBaseException () al final de su cadena para obtener la excepción que lo inició todo.
El uso de la palabra clave "throw" con una excepción siempre restablecerá el seguimiento de la pila.
Lo mejor que puedes hacer es capturar la excepción real que deseas y usar "lanzar"; en lugar de "lanzar ex;". O para lanzar su propia excepción, con la InnerException que desea transmitir.
No creo que lo que quieres hacer sea posible.
Es posible con .net 4.5:
catch(Exception e)
{
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
Es posible conservar la traza de pila antes de volver a generar sin reflexión:
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
}
Esto desperdicia muchos ciclos en comparación con InternalPreserveStackTrace, pero tiene la ventaja de confiar solo en la funcionalidad pública. Aquí hay un par de patrones de uso comunes para las funciones de preservación del seguimiento de la pila:
// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
PreserveStackTrace (e) ;
// store exception to be re-thrown later,
// possibly in a different thread
operationResult.Exception = e ;
}
// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
PreserveStackTrace (tiex.InnerException) ;
// unwrap TargetInvocationException, so that typed catch clauses
// in library/3rd-party code can work correctly;
// new stack trace is appended to existing one
throw tiex.InnerException ;
}
Hay una forma de "restablecer" el seguimiento de la pila en una excepción mediante el uso del mecanismo interno que se utiliza para preservar los seguimientos de la pila del lado del servidor cuando se utiliza la comunicación remota, pero es horrible:
try
{
// some code that throws an exception...
}
catch (Exception exception)
{
FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
remoteStackTraceString.SetValue(exception, exception.StackTrace);
throw exception;
}
Esto coloca la traza de pila original en el campo _remoteStackTraceString
de la excepción, que se concatena a la traza de la pila recién restablecida cuando se relanza la excepción.
Esto es realmente un truco horrible , pero logra lo que quieres. Sin embargo, está haciendo System.Exception
retoques dentro de la clase System.Exception
, por lo que este método puede interrumpirse en versiones posteriores del marco.
No puedes hacer eso. throw
siempre restablece la traza de pila, a menos que se use sin parámetro. Me temo que sus llamadores tendrán que usar la InnerException ...
No, eso no es posible. Su única oportunidad real es seguir el patrón recomendado y lanzar su propia excepción con la InnerException
correspondiente.
Editar
Si su preocupación es la presencia de la TargetInvocationException
y desea TargetInvocationException
(no es que lo recomiende, ya que podría tener algo que ver con el hecho de que se está ejecutando en otro hilo), entonces nada le impide lanzar su Haga su propia excepción aquí y InnerException
la InnerException
de la TargetInvocationException
como su propia InnerException
. Es un poco maloliente, pero podría lograr lo que quieres.