在.NET异常中保留原始堆栈跟踪/行号

时间:2009-10-18 14:56:45

标签: .net exception line-numbers

了解 throw ex throw 之间的区别,为什么在此示例中保留了原始StackTrace:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            SomethingThatThrowsException(x);
        }
        catch (Exception)
        {
            throw;
        }
    }

    static void SomethingThatThrowsException(int x)
    {
        int y = x / (x - x);
    }

但不是在这一个:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            int y = x / (x - 20);
        }
        catch (Exception)
        {
            throw;
        }
    }

第二种情况是产生与投掷前相同的输出?

在这两种情况下,都希望看到y被初始化的行号。

2 个答案:

答案 0 :(得分:17)

我不确定这个限制是否在C#语言,CLI或这些限制的Microsoft实现中,但是您的第二个示例是需要显式调用Exception.InternalPreserveStackTrace的情况,如以下帖子。由于此方法为internal,因此通常必须通过反射调用。通过为呼叫创建Action<Exception>,几乎可以完全缓解此过程中涉及的性能问题,如本答案末尾所示。

参考:Rethrowing exceptions and preserving the full call stack trace

编辑:重新审核ECMA-335分区I§12.4.2(异常处理)和分区III§4.24(重新抛出)后,我现在相信您所看到的行为是语义错误CLR(微软的CLI实现)。对行为的唯一具体引用是“A rethrow不会更改对象中的堆栈跟踪。”在这里描述的情况下,重新抛出事实上正在改变堆栈跟踪,使PreserveStackTrace hack成为了解已知CLR缺陷的解决方法。

static void LongFaultyMethod() 
{ 
    try 
    { 
        int x = 20; 
        int y = x / (x - 20); 
    } 
    catch (Exception ex) 
    { 
        PreserveStackTrace(ex); // <-- add this line
        throw; 
    } 
} 

PreserveStackTrace这是对该博客条目的优化:

private static readonly Action<Exception> _internalPreserveStackTrace =
    (Action<Exception>)Delegate.CreateDelegate(
        typeof(Action<Exception>),
        typeof(Exception).GetMethod(
            "InternalPreserveStackTrace",
            BindingFlags.Instance | BindingFlags.NonPublic));

public static void PreserveStackTrace(Exception e)
{
    _internalPreserveStackTrace(e);
}

答案 1 :(得分:2)

因为在第二个例子中,你是从同一个方法重新抛出异常。首先,它从不同的方法抛出,这就是为什么。在一个方法范围中,堆栈跟踪只能是一个。

执行以下操作,最好的方法是始终在新异常中包含异常,以便您看到异常深度。

  

“如果重新抛出已经发布了   相同的方法(异常堆栈跟踪   只有一个行号信息   每种方法,你永远不会看到堆栈跟踪   在方法A中,在第2行   异常被抛出然后同样   方法A,它是从线上重新抛出的   数字17,它只包含最后一个   异常所在的行号   重新抛出“

try        
{            
   int x = 20;            
   int y = x / (x - 20);        
}        
catch (Exception ex)        
{            
   // do something here.. like log or something
   throw new Exception("Internal Exception", ex);        
}

我很惊讶这么多评论都不读我的评论!!我在评论中写道,你应该安全地记录这个,有各种各样的原因,如果顶级代码吃了异常并且你不知道抛出了哪个和哪个异常,日志记录会帮助你交叉异常!! !

如果您不需要记录,则不要捕获异常。