堆栈跟踪上的行号错误

我有这个代码

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

上面的代码在此代码中调用

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

作为参数传递给方法“HandleException”的exception包含堆栈跟踪中“throw”行的行号,而不是生成exception的实际行。 任何人都知道为什么会发生这种情况?

EDIT1好的,谢谢你的回答。 我更改了内部捕获

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

现在我在堆栈跟踪上有正确的行,但我不得不创建一个新的exception。 我希望找到更好的解决方案:-(

EDIT2也许(如果你有5分钟)你可以尝试这种情况,以检查你是否得到相同的结果,而不是很复杂的重新创建。

是的,这是exception处理逻辑中的限制。 如果一个方法包含多个抛出exception的throw语句,那么你将得到最后一个抛出的行号。 此示例代码重现此行为:

 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 } } } 

输出:

 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 

解决方法很简单,只需使用辅助方法来运行可能引发exception的代码。

像这样:

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

这种尴尬行为的根本原因是.NETexception处理是建立在操作系统对exception的支持之上的。 在Windows中称为SEH,结构化exception处理。 这是基于堆栈帧的,每个堆栈帧只能有一个活动exception。 无论方法内的作用域块数是多少,.NET方法都有一个堆栈帧。 通过使用辅助方法,您可以自动获取另一个可以跟踪其自身exception的堆栈帧。 当方法包含throw语句时,抖动也会自动抑制内联优化,因此不需要显式使用[MethodImpl]属性。

“但扔掉;保留堆栈跟踪!使用throw;

你有多少次听说过…那些已经编程了一段时间.NET的人几乎肯定听过这个并且可能接受它作为所有并且结束所有“重新抛出”的例外。

不幸的是,并非总是如此。 正如@hans所解释的那样,如果导致exception的代码发生在与throw;相同的方法中throw; 然后堆栈跟踪重置到该行。

一种解决方案是在try, catch提取代码try, catch到一个单独的方法,另一种解决方案是抛出一个新exception,将捕获的exception作为内部exception。 一个新方法有点笨拙,如果你试图在调用堆栈中进一步捕获它, new Exception()丢失原始exception类型。

我发现在Fabrice Marguerie的博客上找到了对这个问题的更好的描述。

但更好的是另一个StackOverflow问题有解决方案(即使其中一些涉及reflection):

在C#中,如何在不丢失堆栈跟踪的情况下重新抛出InnerException?

.pdb文件的日期/时间戳是否与.exe / .dll文件匹配? 如果没有,可能是编译不在“调试模式”,它在每个构建上生成一个新的.pdb文件。 发生exception时,pdb文件具有准确的行号。

查看编译设置以确保生成调试数据,或者如果您处于测试/生产环境中,请检查.pdb文件以确保时间戳匹配。

从.NET Framework 4.5开始,您可以使用ExceptionDispatchInfo类来执行此操作,而无需其他方法。 例如,当你使用throw时,借用Hans的优秀答案中的代码,如下所示:

 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 } } } 

它输出:

 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 

但是,您可以使用ExceptionDispatchInfo捕获并重新抛出exception,如下所示:

 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 } } } 

然后它将输出:

 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 

如您所见, ExceptionDispatchInfo.Throw将附加信息附加到原始exception的堆栈跟踪中,添加了它被重新抛出的事实,但它保留了原始行号和exception类型。 有关更多信息,请参阅MSDN文档 。

C#堆栈跟踪是在抛出时生成的,而不是在exception创建时生成的。

这与Java不同,Java在创建exception时填充堆栈跟踪。

这显然是设计上的。

如果选中Optimize code我经常在生产系统中得到它。 即使在2016年,这也会增加行号。

确保您的配置设置为“发布”或您正在构建和部署的任何配置。 该复选框的每个配置具有不同的值

我从来没有最终知道我的代码是如何通过这个检查更加’优化’ – 所以如果你需要检查它 – 但它已经在很多场合保存了我的堆栈跟踪。

在此处输入图像描述