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