2009-10-18 125 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