16

了解throw exthrow的区别,为什么在这个例子中保留了原始的 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;
        }
    }

第二种情况是产生与throw ex相同的输出?

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

4

2 回答 2

18

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

参考:重新抛出异常并保留完整的调用堆栈跟踪

编辑:在重新检查 ECMA-335 Partition I §12.4.2(异常处理)和 Partition III §4.24(重新抛出)之后,我现在认为您看到的行为是 CLR(Microsoft 的 CLI 实现)中的语义错误。对该行为的唯一具体引用是“Arethrow不会更改对象中的堆栈跟踪”。在此处描述的情况下,重新抛出实际上是在更改堆栈跟踪,使PreserveStackTrace黑客成为已知 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);
}
于 2010-04-04T17:55:23.367 回答
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);        
}

我对这么多没有阅读我的评论的评论感到惊讶!我在评论中写道,您可能应该安全地记录这个,有各种原因,如果顶级代码吃掉异常并且您不知道抛出了哪个和哪里的异常,日志记录可以帮助您交叉异常!

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

于 2009-10-18T15:02:28.697 回答