2010-03-22 22 views
27

tengo este códigonúmero de línea incorrecta en seguimiento de la pila

try 
{ 
    //AN EXCEPTION IS GENERATED HERE!!! 
} 
catch 
{ 
    SqlService.RollbackTransaction(); 
    throw; 
} 

Código anterior que se llama en este código

try 
{ 
    //HERE IS CALLED THE METHOD THAT CONTAINS THE CODE ABOVE 
} 
catch (Exception ex) 
{ 
    HandleException(ex); 
} 

La excepción se pasa como parámetro al método "HandleException" contiene el número de línea de la línea "throw" en el seguimiento de la pila en lugar de la línea real donde se generó la excepción. ¿Alguien sabe por qué esto podría estar pasando?

EDIT1 Ok, gracias a todos por sus respuestas. Me cambió la captura interior para


catch(Exception ex) 
{ 
    SqlService.RollbackTransaction(); 
    throw new Exception("Enrollment error", ex); 
} 

ahora tengo la línea correcta en el seguimiento de la pila, pero tenía que crear una nueva excepción. Tenía la esperanza de encontrar una mejor solución :-(

Edit2 Tal vez (si usted tiene 5 minutos) puede probar este escenario con el fin de comprobar si se obtiene el mismo resultado, no es muy complicado de recrear.

+5

¿Estás seguro de que no dice 'throw ex;'? – SLaks

+0

100% seguro, raro: -s –

+2

¿La excepción original está siendo ocultada por uno tal vez lanzado por SqlService.RollbackTransaction? –

Respuesta

34

Sí, esto es una limitación en la lógica de manejo de excepciones. Si un método contiene más de una instrucción throw que arroja una excepción, obtendrá el número de línea del último que arrojó. Este código de ejemplo reproduce este comportamiento:

using System; 

class Program { 
    static void Main(string[] args) { 
     try { 
      Test(); 
     } 
     catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
     } 
     Console.ReadLine(); 
    } 
    static void Test() { 
     try { 
      throw new Exception(); // Line 15 
     } 
     catch { 
      throw;     // Line 18 
     } 
    } 
} 

Salida:

System.Exception: Exception of type 'System.Exception' was thrown. 
    at Program.Test() in ConsoleApplication1\Program.cs:line 18 
    at Program.Main(String[] args) in ConsoleApplication1\Program.cs:line 6 

La solución alternativa es simple, sólo tiene que utilizar un método de ayuda para ejecutar el código que podría lanzar una excepción.

De esta manera:

static void Test() { 
    try { 
     Test2();    // Line 15 
    } 
    catch { 
     throw;     // Line 18 
    } 
} 
static void Test2() { 
    throw new Exception();  // Line 22 
} 

La razón subyacente de este comportamiento extraño es que el manejo de excepciones .NET se construye en la parte superior del soporte del sistema operativo para las excepciones. Llamado SEH, manejo estructurado de excepciones en Windows. Que se basa en el stack-frame, solo puede haber una excepción activa por stack frame. Un método .NET tiene un marco de pila, independientemente de la cantidad de bloques de alcance dentro del método. Al usar el método de ayuda, automáticamente obtienes otro marco de pila que puede rastrear su propia excepción. El jitter también suprime automáticamente la optimización de alineación cuando un método contiene una instrucción throw, por lo que no es necesario utilizar explícitamente el atributo [MethodImpl].

+0

¡Gracias! No estoy seguro de lo que quiere decir con ayuda r método. La excepción en mi código y en tu código se genera en un método separado. –

+1

@Claudio: Actualicé la publicación para mostrar cómo se ve el método de ayuda. –

4

C rastros # pila se generan en el momento de lanzamiento, no en tiempo de creación excepción.

Esto es diferente de Java, donde las trazas de la pila se llenan durante la creación excepción.

Esto es al parecer por diseño.

+1

Sí, pero 'throw;' preserva el trazo original de la pila – SLaks

+0

@SLaks: punto excelente. Basado en el comentario de @Andy Shellam a la pregunta original, estoy empezando a pensar que tal vez la excepción original sea tragada por una excepción en el bloque catch. – Randolpho

5

¿El sello de fecha y hora de su archivo .pdb coincide con su archivo .exe/.dll? De lo contrario, podría ser que la compilación no esté en el "modo de depuración", que genera un archivo .pdb nuevo en cada compilación. El archivo pdb tiene los números de línea precisos cuando se producen excepciones.

Mire en la configuración de compilación para asegurarse de que se generan los datos de depuración, o si se encuentra en un entorno de prueba/producción, verifique el archivo .pdb para asegurarse de que las marcas de tiempo coincidan.

+0

posible lado interesante respuesta. +1 – Randolpho

+0

me ha sucedido en más de una ocasión. 8^D –

+0

fechas coinciden, simplemente doble control –

10

"Pero tiro; conserva el seguimiento de la pila !! Use tiro;"

¿Cuántas veces has oído que ... Bueno cualquiera que haya sido la programación .NET de un mientras que casi con certeza ha escuchado eso y probablemente lo haya aceptado como el principio de todas las excepciones de "reintroducción".

Desafortunadamente no siempre es así. Como @hans explica, si el código que causa la excepción ocurre en el mismo método que la declaración throw;, entonces el rastreo de la pila se restablece a esa línea.

Una solución es extraer el código dentro del try, catch en un método separado, y otra solución es lanzar una nueva excepción con la excepción atrapada como una excepción interna. Un nuevo método es un poco torpe, y new Exception() pierde el tipo de excepción original si intenta atraparlo más arriba en la pila de llamadas.

Encontré una mejor descripción de este problema en Fabrice Marguerie's blog.

pero aún mejor hay otra pregunta StackOverflow que tiene soluciones (aunque algunas de ellas implican la reflexión):

  In C#, how can I rethrow InnerException without losing stack trace?

+0

+1 Simon Gracias por su respuesta :) Voy a consultar esos enlaces –

3

menudo consigo esto en sistemas de producción si no se marca Optimize code. Esto arruina los números de línea incluso en 2016.

Asegúrese de que su configuración esté configurada en 'Liberar' o en la configuración que esté construyendo y desplegando. La casilla de verificación tiene un valor diferente por configuración

En última instancia, nunca sé cuánto más "optimizado" es mi código con esta marcada, así que revísela si es necesario, pero ha guardado mi seguimiento de pila en muchas ocasiones.

enter image description here

2

A partir de .NET Framework 4.5 se puede utilizar la clase ExceptionDispatchInfo hacer esto sin la necesidad de otro método. Por ejemplo, tomando prestado el código de respuesta excelente de Hans, cuando sólo tiene que utilizar throw, así:

using System; 

class Program { 
    static void Main(string[] args) { 
     try { 
      Test(); 
     } 
     catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
     } 
     Console.ReadLine(); 
    } 
    static void Test() { 
     try { 
      throw new ArgumentException(); // Line 15 
     } 
     catch { 
      throw;       // Line 18 
     } 
    } 
} 

Genera esto:

System.ArgumentException: Value does not fall within the expected range. 
    at Program.Test() in Program.cs:line 18 
    at Program.Main(String[] args) in Program.cs:line 6 

embargo, puede utilizar ExceptionDispatchInfo para capturar y volver emitir la excepción, así:

using System; 

class Program { 
    static void Main(string[] args) { 
     try { 
      Test(); 
     } 
     catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
     } 
     Console.ReadLine(); 
    } 
    static void Test() { 
     try { 
      throw new ArgumentException();    // Line 15 
     } 
     catch(Exception ex) { 
      ExceptionDispatchInfo.Capture(ex).Throw(); // Line 18 
     } 
    } 
} 

A continuación se dará salida a esto:

System.ArgumentException: Value does not fall within the expected range. 
    at Program.Test() in Program.cs:line 15 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at Program.Test() in Program.cs:line 18 
    at Program.Main(String[] args) in Program.cs:line 6 

Como puede ver, ExceptionDispatchInfo.Throw agrega información adicional al rastro de la pila de la excepción original, agregando el hecho de que fue relanzada, pero conserva el número de línea original y el tipo de excepción. Consulte el MSDN documentation para obtener más información.

Cuestiones relacionadas