为什么堆栈的高位(在Exception.StackTrace中)会被截断? 让我们看一个简单的例子:
public void ExternalMethod()
{
InternalMethod();
}
public void InternalMethod()
{
try
{
throw new Exception();
}
catch(Exception ex)
{
// ex.StackTrace here doesn't contain ExternalMethod()!
}
}
这似乎是“按设计”。但这种奇怪设计的原因是什么?它只会使调试变得更复杂,因为在日志消息中我无法理解谁调用了InternalMethod(),而且这些信息通常是非常必要的。
至于解决方案(对于那些不知道的人),我理解有两种通用解决方案:
1)我们可以记录静态Environment.StackTrace属性,它包含整个堆栈(例如,从hiest级别(消息队列)开始并以发生异常的最深层方法结束)。
2)我们必须捕获并记录最高级别的异常。当我们需要捕获较低级别的异常来做某事时,我们需要重新抛出(在C#中使用“throw”语句)它会进一步上升。
但问题是这种设计的原因。
答案 0 :(得分:11)
好的,现在我看到你得到了什么...抱歉我对内联的事情感到困惑。
捕获的异常中的“堆栈”只是从当前正在执行的catch块到抛出异常的位置的增量。从概念上讲,这种行为是正确的,因为Exception.StackTrack会告诉您在此try / catch块的上下文中发生异常的位置。这允许异常堆栈在“虚拟”调用之间转发,并仍然保持准确性。这样做的一个典型例子是.Net Remoting异常。
因此,如果您想在catch块中获得完整的堆栈报告,则可以将当前堆栈添加到异常堆栈中,如下例所示。唯一的问题是这可能会更贵。
private void InternalMethod()
{
try
{
ThrowSomething();
}
catch (Exception ex)
{
StackTrace currentStack = new StackTrace(1, true);
StackTrace exceptionStack = new StackTrace(ex, true);
string fullStackMessage = exceptionStack.ToString() + currentStack.ToString();
}
}
答案 1 :(得分:2)
正如csharptest所说这是设计的。 StackTrace在try块停止。更进一步说,在抛出异常时调用框架中没有钩子。
所以你能做的最好的事情就是这些,它是获得完整堆栈跟踪的绝对要求(在创建异常时存储完整的跟踪):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Diagnostics;
namespace ConsoleApplication15 {
[global::System.Serializable]
public class SuperException : Exception {
private void SaveStack() {
fullTrace = Environment.StackTrace;
}
public SuperException() { SaveStack(); }
public SuperException(string message) : base(message) { SaveStack(); }
public SuperException(string message, Exception inner) : base(message, inner) { SaveStack(); }
protected SuperException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
private string fullTrace;
public override string StackTrace {
get {
return fullTrace;
}
}
}
class Program {
public void ExternalMethod() {
InternalMethod();
}
public void InternalMethod() {
try {
ThrowIt();
} catch (Exception ex) {
Console.WriteLine(ex.StackTrace);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void ThrowIt() {
throw new SuperException();
}
static void Main(string[] args) {
new Program().ExternalMethod();
Console.ReadKey();
}
}
}
输出:
at System.Environment.get_StackTrace() at ConsoleApplication15.SuperException..ctor() in C:\Users\sam\Desktop\Source \ConsoleApplication15\ConsoleApplication15\Program.cs:line 17 at ConsoleApplication15.Program.ThrowIt() in C:\Users\sam\Desktop\Source\Cons oleApplication15\ConsoleApplication15\Program.cs:line 49 at ConsoleApplication15.Program.InternalMethod() in C:\Users\sam\Desktop\Sour ce\ConsoleApplication15\ConsoleApplication15\Program.cs:line 41 at ConsoleApplication15.Program.Main(String[] args) in C:\Users\sam\Desktop\S ource\ConsoleApplication15\ConsoleApplication15\Program.cs:line 55 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C ontextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
不可能将这种行为注入到现有的系统定义的异常中,但.Net有一个丰富的基础设施来包装异常和重新抛出,所以它不应该是一个大问题。
答案 2 :(得分:0)
我知道如果你执行throw ex;
,它会在catch块中截断堆栈跟踪。它可能是“按设计”抛出,因为只有throw;
不会截断catch中的堆栈。同样可能会在这里发生,因为你要抛出一个新的异常。
如果您导致实际异常(即int i = 100/0;
)会怎样?堆栈跟踪是否仍然被截断?
答案 3 :(得分:-1)
这通常是由编译器优化引起的。
您可以使用以下属性修饰您不想内联的方法:
[MethodImpl(MethodImplOptions.NoInlining)]
public void ExternalMethod()
{
InternalMethod();
}