Cla*_*edi 30 c# exception-handling try-catch
我有这个代码
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);
}
作为参数传递给方法"HandleException"的异常包含堆栈跟踪中"throw"行的行号,而不是生成异常的实际行.任何人都知道为什么会发生这种情况?
编辑1 好的,谢谢大家的答案.我更改了内部捕获
catch(Exception ex)
{
SqlService.RollbackTransaction();
throw new Exception("Enrollment error", ex);
}
现在我在堆栈跟踪上有正确的行,但我不得不创建一个新的异常.我希望找到更好的解决方案:-(
EDIT2 也许(如果你有5分钟)你可以尝试这种情况,以检查你是否得到相同的结果,而不是很复杂的重新创建.
Han*_*ant 36
是的,这是异常处理逻辑中的限制.如果一个方法包含多个抛出异常的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
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
解决方法很简单,只需使用辅助方法来运行可能引发异常的代码.
像这样:
static void Test() {
try {
Test2(); // Line 15
}
catch {
throw; // Line 18
}
}
static void Test2() {
throw new Exception(); // Line 22
}
Run Code Online (Sandbox Code Playgroud)
这种尴尬行为的根本原因是.NET异常处理建立在操作系统对异常的支持之上.在Windows中称为SEH,结构化异常处理.这是基于堆栈帧的,每个堆栈帧只能有一个活动异常.无论方法内的作用域块数是多少,.NET方法都有一个堆栈帧.通过使用帮助器方法,您可以自动获取另一个可以跟踪其自身异常的堆栈帧.当方法包含throw语句时,抖动也会自动抑制内联优化,因此不需要显式使用[MethodImpl]属性.
Sim*_*ver 11
"但扔掉;保留堆栈跟踪!使用throw; "
你有多少次听说过...那些已经编程.NET一段时间的人几乎肯定已经听过这个并且可能接受它作为所有并且结束所有"重新抛出"异常.
不幸的是,并非总是如此.正如@hans所解释的,如果导致异常的代码发生在与throw;语句相同的方法中,则堆栈跟踪将重置为该行.
一种解决方案是将内部代码提取try, catch到一个单独的方法中,另一种解决方案是将捕获的异常作为内部异常抛出一个新异常.一个新方法有点笨拙,new Exception()如果你试图在调用堆栈中进一步捕获它,则丢失原始异常类型.
我发现在Fabrice Marguerie的博客上发现了对这个问题的更好描述.
但更好的是另一个StackOverflow问题有解决方案(即使其中一些涉及反射):
在C#中,如何在不丢失堆栈跟踪的情况下重新抛出InnerException?
从.NET Framework 4.5开始,您可以使用ExceptionDispatchInfo该类来执行此操作,而无需其他方法.例如,当你刚刚使用时,借用Hans的优秀答案中的代码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 ArgumentException(); // Line 15
}
catch {
throw; // Line 18
}
}
}
Run Code Online (Sandbox Code Playgroud)
它输出这个:
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
Run Code Online (Sandbox Code Playgroud)
但是,您可以使用ExceptionDispatchInfo捕获和重新抛出异常,如下所示:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后它将输出:
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
Run Code Online (Sandbox Code Playgroud)
如您所见,ExceptionDispatchInfo.Throw将附加信息附加到原始异常的堆栈跟踪中,添加它被重新抛出的事实,但它保留了原始行号和异常类型.有关更多信息,请参阅MSDN文档.
.pdb文件的日期/时间戳是否与.exe/.dll文件匹配?如果没有,可能是编译不在"调试模式",它在每个构建上生成一个新的.pdb文件.发生异常时,pdb文件具有准确的行号.
查看编译设置以确保生成调试数据,或者如果您处于测试/生产环境中,请检查.pdb文件以确保时间戳匹配.