实际上,throw 语句在某些情况下不会保留 StackTrace 信息。例如,在下面的代码中:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
StackTrace 将表明第 54 行引发了异常,尽管它是在第 47 行引发的。
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at Program.WithThrowIncomplete() in Program.cs:line 54
at Program.Main(String[] args) in Program.cs:line 106
在上述情况下,有两个选项可以保留原始 StackTrace:
调用 Exception.InternalPreserveStackTrace
由于是私有方法,所以必须使用反射来调用:
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
我的缺点是依赖私有方法来保存 StackTrace 信息。它可以在 .NET Framework 的未来版本中进行更改。上面的代码示例和下面提出的解决方案摘自Fabrice MARGUERIE weblog。
调用 Exception.SetObjectData
Anton Tykhyy 建议使用以下技术作为对In C#, how can I rethrow InnerException without losing stack trace 问题的回答。
static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;
e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData
// voila, e is unmodified save for _remoteStackTraceString
}
虽然它具有仅依赖于公共方法的优点,但它还依赖于以下异常构造函数(第三方开发的某些异常未实现):
protected Exception(
SerializationInfo info,
StreamingContext context
)
在我的情况下,我不得不选择第一种方法,因为我使用的第 3 方库引发的异常没有实现这个构造函数。