Estoy llamando, a través de la reflexión, un método que puede causar una excepción. ¿Cómo puedo pasar la excepción a mi interlocutor sin que el reflejo del contenedor lo coloque?
Estoy volviendo a lanzar la InnerException, pero esto destruye la traza de la pila.
Código de ejemplo:
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
Respuestas:
En .NET 4.5 ahora existe la
ExceptionDispatchInfo
clase.Esto le permite capturar una excepción y volver a lanzarla sin cambiar el seguimiento de la pila:
Esto funciona en cualquier excepción, no solo
AggregateException
.Se introdujo debido a la
await
función de lenguaje C #, que desenvuelve las excepciones internas de lasAggregateException
instancias para hacer que las funciones de lenguaje asíncrono se parezcan más a las funciones de lenguaje síncrono.fuente
throw;
después de la línea .Throw (), porque el compilador no sabrá que .Throw () siempre arroja una excepción.throw;
nunca se llamará como resultado, pero al menos el compilador no se quejará si su método requiere un objeto de retorno o es una función asíncrona.throw;
. Si usathrow ex.InnerException;
el stack-trace se reinicia en el punto en que se vuelve a lanzar.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Que es posible preservar el seguimiento de la pila antes de Regeneración de sin reflexión:
Esto desperdicia muchos ciclos en comparación con las llamadas a
InternalPreserveStackTrace
través de un delegado en caché, pero tiene la ventaja de depender solo de la funcionalidad pública. Aquí hay un par de patrones de uso comunes para las funciones de preservación de seguimiento de pila:fuente
InternalPreserveStackTrace
(aproximadamente un 6% más lento con 10000 iteraciones). Acceder a los campos directamente por reflexión es aproximadamente un 2,5% más rápido que invocarloInternalPreserveStackTrace
e.Data
diccionario con una cadena o una clave de objeto única (static readonly object myExceptionDataKey = new object ()
pero no haga esto si tiene que serializar excepciones en cualquier lugar). Evite modificare.Message
, porque podría tener código en algún lugar que analizae.Message
. El análisise.Message
es malo, pero puede que no haya otra opción, por ejemplo, si tiene que usar una biblioteca de terceros con prácticas de excepción deficientes.Creo que su mejor apuesta sería simplemente poner esto en su bloque de captura:
Y luego extraiga la innerexcepción más tarde.
fuente
Nadie ha explicado la diferencia entre
ExceptionDispatchInfo.Capture( ex ).Throw()
y una simplethrow
, así que aquí está.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 de línea delthrow new Exception( "TEST" )
línea 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
Llame al método de extensión en su excepción antes de lanzarlo, conservará el seguimiento de la pila original.
fuente
Action<Exception>
? Aquí use el método estáticoAún más reflexión ...
Tenga en cuenta que esto puede romperse en cualquier momento, ya que los campos privados no son parte de la API. Ver más discusión sobre Mono bugzilla .
fuente
InternalPreserveStackTrace
método interno sería mejor, ya que hace lo mismo y es menos probable que cambie en el futuro ...Primero: no pierda la TargetInvocationException: es información valiosa cuando querrá depurar cosas.
Segundo: Envuelva el TIE como InnerException en su propio tipo de excepción y coloque una propiedad OriginalException que se vincule a lo que necesita (y mantenga intacta toda la pila de llamadas).
Tercero: deje que el TIE salga de su método.
fuente
Chicos, ustedes son geniales ... pronto seré nigromante.
fuente
.Invoke()
.Otro código de muestra que utiliza serialización / deserialización de excepción. No requiere que el tipo de excepción real sea serializable. También utiliza solo métodos públicos / protegidos.
fuente
Basado en la respuesta de Paul Turners, hice un método de extensión
return ex
nunca se llegó a la meta, pero la ventaja es que puedo usarlothrow ex.Capture()
como un trazador de líneas para que el compilador no genere unnot all code paths return a value
error.fuente