2009-10-18 62 views
13

了解罚球前之间的区别保留原来的堆栈跟踪/ LineNumbers,为什么原来的堆栈跟踪保存在这个例子中:在.NET异常

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被初始化的行号。

回答

14

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

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

编辑:重新审视ECMA-335分区后,我§12.4.2(异常处理)和分区III§4.24(重新抛出),现在我相信你所看到的行为是一个CLR中的语义错误(Microsoft的CLI实现)。对此行为的唯一具体引用是“A rethrow不会更改对象中的堆栈跟踪。”在这里描述的情况下,重新抛出实际上是改变堆栈跟踪,使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); 
} 
+0

这很好,谢谢你的信息。 – Nariman 2010-04-05 14:37:46

3

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

做如下,最好的办法是总是在新的异常中包装一个异常,这样你会看到异常深度。

“如果重投已在 同样的方法(异常堆栈跟踪发出 只有一个行号信息 每个方法,你永远看不到堆栈跟踪 方法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);   
} 
0123最后 行号
+0

我明白,这是一个变通的作品,但我仍然不明白的,为什么重新抛出第二个例子不应该有正确的行号。 – Sam 2009-10-18 16:20:15

+0

这不是一种解决方法。这是做它的正确方法... – awe 2009-11-13 13:13:56

+0

@Akash Kava:你有参考吗?我不相信你完全明白OP的问题。 – 2010-04-04 17:48:42