如果异常被捕获然后重新抛出,那么它的堆栈跟踪将显示为好像异常发生在重新抛出的点而不是原始位置。希望您的代码中不会发生太多这样的事情。如果不是,那么仅堆栈跟踪就足以区分异常位置。
另一个问题是堆栈跟踪是否相同,因此当它们实际上相同时,它们看起来不会是两个不同的异常。这也不应该是一个问题,因为根据我的经验,堆栈跟踪是相同的。 (异常对象上的 ToString() 结果通常不会相同。)
更新
关于Exception.StackTrace 将被损坏的具体情况已经有一些讨论。基本规则是随时
throw *expression*;
被执行,然后*expression* 标识的Exception 对象将设置其StackTrace 属性。如果*expression* 被省略
throw;
那么 StackTrace 将不会受到影响。
我找不到这两种形式的任何术语,所以我将它们分别称为“显式”和“隐式”。请注意,*expression* 解析为的异常对象是 new 还是已经存在的对象都没有关系。
这是一个说明这种行为的程序:
using System;
namespace FunWithExceptions
{
class Program
{
static void Main(string[] args)
{
try { Replace(); }
catch (InvalidOperationException ex) { DisplayResult("Replace resulted in", ex); }
try { RethrowExplicit(); }
catch (InvalidOperationException ex) { DisplayResult("RethrowExplicit resulted in", ex); }
try { RethrowImplicit(); }
catch (InvalidOperationException ex) { DisplayResult("RethrowImplicit resulted in", ex); }
InvalidOperationException myException = new InvalidOperationException();
DisplayResult("myException starts with", myException);
try { throw myException; }
catch (InvalidOperationException) { }
DisplayResult("myException changes to", myException);
Console.ReadLine();
}
static void ThrowAnException()
{ throw new InvalidOperationException("You messed up!"); }
static void Replace()
{
try { ThrowAnException(); }
catch (InvalidOperationException ex)
{
DisplayResult("Replace caught", ex);
throw new InvalidOperationException("Another mistake.");
}
}
static void RethrowExplicit()
{
try { ThrowAnException(); }
catch (InvalidOperationException ex)
{
DisplayResult("RethrowExplicit caught", ex);
throw ex;
}
}
static void RethrowImplicit()
{
try { ThrowAnException(); }
catch (InvalidOperationException ex)
{
DisplayResult("RethrowImplicit caught", ex);
throw;
}
}
static void DisplayResult(string context, Exception ex)
{
Console.WriteLine("{0} exception thrown at {1}", context, FirstMethodName(ex.StackTrace));
}
private const string methodNamePrefix = " at FunWithExceptions.Program.";
private static object FirstMethodName(string stackTrace)
{
stackTrace = stackTrace ?? string.Empty;
if (stackTrace.StartsWith(methodNamePrefix))
stackTrace = stackTrace.Substring(methodNamePrefix.Length);
int methodNameEndIndex = stackTrace.IndexOf(')');
if (methodNameEndIndex != -1)
stackTrace = stackTrace.Substring(0, methodNameEndIndex + 1);
if (stackTrace.Length > 0)
return stackTrace;
else
return "--empty--";
}
}
}
此程序产生以下输出:
替换在 ThrowAnException() 抛出的捕获的异常
Replace 导致 Replace() 抛出异常
RethrowExplicit 捕获 ThrowAnException() 抛出的异常
RethrowExplicit 导致在 RethrowExplicit() 抛出异常
RethrowImplicit 捕获 ThrowAnException() 抛出的异常
RethrowImplicit 导致 ThrowAnException() 抛出异常
myException 以在 --empty-- 处引发的异常开始
myException 更改为 Main(String[] args) 引发的异常
第六行很有意思。
在那里,我现在已经彻底击败了这一点。 :-)