2009-11-11 85 views
10

为什么堆栈的高部分(在Exception.StackTrace中)被截断? 让我们来看看一个简单的例子:为什么堆栈在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属性(例如,从最高级别(消息队列)并以发生异常的最深方法结束)。
2)我们必须捕捉并记录最高级别的异常。当我们需要在低层次上捕捉异常来做某些事情时,我们需要重新抛出(用C#中的“抛出”语句)它进一步。

但问题是关于这种设计的原因。

+0

为什么要对谁把它称为一个对象护理?你的观点(2),即应该重新抛出异常,是正确的方法。 – 2009-11-11 22:21:15

+0

主要堆栈跟踪信息包含在例外中以用于调试目的。使用现有的设计,它不会像调试堆栈的高部分那样帮助调试。因为我再说一遍,知道谁调用这个方法对调试非常有用。 – nightcoder 2009-11-11 22:24:52

回答

0

我知道在一个catch块中,如果你做throw ex;它会在那个时候截断栈跟踪。这可能是“按设计”投掷,因为只有throw;不会截断抓取中的堆栈。因为你抛出一个新的异常,所以可能会发生在这里。

如果导致实际异常(即int i = 100/0;),会发生什么情况?堆栈跟踪是否仍然被截断?

+1

'''抛出;'''截断协议栈以及 – Enerccio 2017-03-04 09:23:21

-1

这通常是由编译器优化造成的。

可以装饰你不想使用下列属性联方法:

[MethodImpl(MethodImplOptions.NoInlining)] 
public void ExternalMethod() 
{ 
    InternalMethod(); 
} 
+0

没有,这是因为内联的不行。我所说的行为是默认行为。你可以自己看看。它将永远如我所示。 – nightcoder 2009-11-11 22:42:17

+1

为什么不是JIT内联总是以可预测的方式方法?也许你的方法总是被内联。 – 2009-11-12 00:45:37

+0

,它总是这样,在没有办法的功能,证明它是不是由于内联的事实。 – 2009-11-12 01:46:09

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(); 
     } 
    } 
+0

+1这就是原因,请参阅我的答案以解决方法 – 2009-11-12 02:10:40

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() 

这是不可能注入这种行为到现有的系统定义的异常,但净有对包装异常,并重新抛出所以它不应该是一个巨大的交易了丰富的基础设施。

+1

这是一种替代方法;然而,它不会指出抛出异常的地方,但它在哪里被构造(通常是相同的东西,所以这并不坏)。如果你走这条路,意识到这将是整个呼叫上下文准确(如远程),并也将需要额外的代码序列化“fullTrace”属性。 – 2009-11-12 03:35:45

+0

发表评论。 – 2009-11-12 05:15:36