什么可以导致重置一个callstack(我正在使用“throw”,而不是“throw ex”)
我一直认为“throw”和“throw ex”之间的区别在于,单独抛出并没有重置exception的堆栈跟踪。
不幸的是,这不是我正在经历的行为; 这是一个简单的样本再现我的问题:
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(); } } }
我希望这段代码能够从第14行开始打印一个callstack; 然而,callstack从第18行开始。当然,它在样本中没什么大不了的,但在我的实际应用程序中,丢失初始错误信息非常痛苦。
我错过了一些明显的东西吗 有没有其他方法来实现我想要的(即重新抛出exception而不丢失堆栈信息?)
我正在使用.net 3.5
你应该阅读这篇文章:
- 重新抛出exception并保留完整的调用堆栈跟踪
简而言之, throw
通常会保留原始抛出exception的堆栈跟踪,但前提是当前堆栈框架中没有发生exception(即方法)。
您使用的方法PreserveStackTrace
(在博客文章中显示)保留了原始堆栈跟踪,如下所示:
try { } catch (Exception ex) { PreserveStackTrace(ex); throw; }
但我通常的解决方案是要么不捕获并重新抛出这样的exception(除非绝对必要),要么只是总是使用InnerException
属性抛出新的InnerException
来传播原始exception:
try { } catch (Exception ex) { throw new Exception("Error doing foo", ex); }
问题是Windows正在重置堆栈的起点。 CLR的行为与预期一致 – 这只是主机操作系统exception处理支持的限制。 问题是每个方法调用只能有一个堆栈帧。
您可以将exception处理例程提取到单独的“帮助程序”方法中,这可以解决Windows SEH所施加的限制,但我认为这不一定是个好主意。
在不丢失堆栈信息的情况下重新抛出exception的正确方法是抛出一个新exception并将原始的捕获exception作为内部exception包含在内。
很难想象很多情况下你真的需要这样做。 如果你没有处理exception,只是抓住它来重新抛出它,你可能不应该首先捕获它。
正常的重新抛出会保留堆栈跟踪中的所有内容,但如果当前方法位于堆栈跟踪中,则行号将被覆盖。 这是令人讨厌的行为。 在C#中,如果需要在特殊情况下执行某些操作但不关心exception是什么,可以使用该模式:
Boolean ok = False; 尝试 { 做一点事(); ok = True; } 最后 { if(!ok)//发生exception! handle_exception(); }
有一些数字,这种模式非常有用; 最常见的是一个应该返回一个新的IDisposable的函数。 如果该function不会返回,则必须清理一次性物体。 请注意,上述“try”块中的任何“return”语句必须将ok设置为true 。
在vb.net中,可以使用function上更好一点的模式,虽然代码中的一个点有点icky,具有以下模式:
Dim PendingException As Exception = Nothing; 尝试 做一点事 PendingException = Nothing'参见注释 在CopyFirstParameterToSecondAndReturnFalse(Ex,PendingException)时捕获Ex Exception 扔'永远不会执行,因为上面会返回错误 最后 如果PendingException IsNot Nothing那么 ..处理例外 万一 结束尝试
长命名的function应该以明显的方式实现。 此模式的优点是可以使代码可用。 虽然在处理但不捕获的情况下通常不需要这样做,但有一种情况可能是非常宝贵的:如果清理例程抛出exception。 通常,如果清理例程抛出exception,则任何挂起的exception都将丢失。 但是,使用上述模式,可以将挂起的exception包装在清理exception中。
上面代码的一个有趣的注意事项:exception可以到达“Catch When”,但Try语句可以正常完成。 目前还不清楚在这种情况下应该发生什么,但有一点很明显,即终止声明不应该表现为exception待决。 清除PendingException将使它如果exception消失,代码将表现得好像从未发生过。 另一种方法是包装并重新抛出已知发生的exception,因为这种情况几乎肯定表明内部exception处理代码有问题。