c# .net exception exception-handling stack-trace

c# - ¿Qué puede llevar tirar para restablecer una pila de llamadas(estoy usando "lanzar", no "lanzar ex")



.net exception (3)

Deberías leer este artículo:

En resumen, throw normalmente conserva la traza de la pila de la excepción arrojada original, pero solo si la excepción no se produjo en el marco de la pila actual (es decir, el método).

Hay un método PreserveStackTrace (que se muestra en ese artículo de blog) que utiliza que conserva el seguimiento de la pila original como este:

try { } catch (Exception ex) { PreserveStackTrace(ex); throw; }

Pero mi solución habitual es simplemente no capturar y volver a lanzar excepciones como esta (a menos que sea absolutamente necesario), o simplemente lanzar siempre nuevas excepciones usando la propiedad InnerException para propagar la excepción original:

try { } catch (Exception ex) { throw new Exception("Error doing foo", ex); }

Siempre pensé que la diferencia entre "throw" y "throw ex" era que el lanzamiento por sí solo no estaba restableciendo la stacktrace de la excepción.

Desafortunadamente, ese no es el comportamiento que estoy experimentando; Aquí hay una muestra simple que reproduce mi problema:

using System; using System.Text; namespace testthrow2 { class Program { static void Main(string[] args) { try { try { throw new Exception("line 14"); } catch (Exception) { throw; // line 18 } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.ReadLine(); } } }

Esperaría que este código imprimiera una pila de llamadas comenzando en la línea 14; sin embargo, la pila de llamadas comienza en la línea 18. Por supuesto, no es gran cosa en la muestra, pero en mi aplicación de la vida real, perder la información de error inicial es bastante doloroso.

¿Me estoy perdiendo algo obvio? ¿Hay alguna otra manera de lograr lo que quiero (es decir, volver a lanzar una excepción sin perder la información de la pila?)

Estoy usando .net 3.5


El problema es que Windows está restableciendo el punto de partida de la pila. El CLR se está comportando como se esperaba, esto es solo una limitación del soporte de manejo de excepciones del sistema operativo host. El problema es que solo puede haber un marco de pila por llamada de método.

Podría extraer sus rutinas de manejo de excepciones en un método de "ayuda" separado, que funcionaría en torno a las limitaciones impuestas por el SEH de Windows, pero no creo que sea necesariamente una buena idea.

La forma adecuada de volver a lanzar una excepción sin perder la información de la pila es lanzar una nueva excepción e incluir la excepción original capturada como la excepción interna.

Es difícil imaginar muchos casos en los que realmente necesitarías hacer esto. Si no está manejando la excepción, y simplemente atrapándola para volver a lanzarla, probablemente no debería atraparla en primer lugar.


El retorno normal conserva todo en el seguimiento de la pila, excepto que si el método actual está en el seguimiento de pila, se sobrescribirá el número de línea. Este es un comportamiento molesto. En C # si uno necesita hacer algo en el caso excepcional pero no le importa cuál es la excepción, uno puede usar el patrón:

Boolean ok = False; try { do_something(); ok = True; } finally { if (!ok) // An exception occurred! handle_exception(); }

Hay un número donde ese patrón es muy útil; la más común sería una función que se supone que devuelve un nuevo IDisposable. Si la función no va a regresar, el objeto desechable debe limpiarse. Tenga en cuenta que cualquier declaración "return" dentro del bloque "try" anterior debe establecerse como verdadero .

En vb.net, es posible utilizar un patrón que es funcionalmente un poco más agradable, aunque un punto en el código es un poco asqueroso, con el patrón:

Dim PendingException As Exception = Nothing; Try Do_Something PendingException = Nothing '' See note Catch Ex As Exception When CopyFirstParameterToSecondAndReturnFalse(Ex, PendingException ) Throw '' Will never execute, since above will return false Finally If PendingException IsNot Nothing Then .. Handle exception EndIf End Try

La función largamente nombrada debe implementarse de la manera obvia. Este patrón tiene la ventaja de hacer que la excepción esté disponible para el código. Si bien eso no se necesita a menudo en situaciones de manejo y no captura, hay una situación en la que puede ser muy valiosa: si una rutina de limpieza arroja una excepción. Normalmente, si una rutina de limpieza arroja una excepción, se perderá cualquier excepción pendiente. Sin embargo, con el patrón anterior, es posible ajustar la excepción pendiente dentro de la excepción de limpieza.

Una nota interesante con el código anterior: es posible que una excepción llegue al "Catch When" y, sin embargo, que la declaración Try se complete normalmente. Realmente no está claro qué debería suceder en esa circunstancia, pero una cosa que está clara es que la declaración Finally no debe actuar como si una excepción estuviera pendiente. La eliminación de PendingException lo hará de modo que si una excepción desaparece, el código se comporte como si nunca hubiera sucedido. Una alternativa sería ajustar y volver a lanzar una excepción que se sabe que ocurrió, ya que esa situación casi con certeza indica algo erróneo con el código interno de manejo de excepciones.