在.NET异常中保留原始StackTrace/LineNumbers

Sam*_*Sam 15 .net exception line-numbers

理解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);
    }
Run Code Online (Sandbox Code Playgroud)

但不是在这一个:

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

Sam*_*ell 17

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

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

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)