2013-02-04 43 views
13

我讀過,當在catch塊中時,我可以使用「throw」重新拋出當前異常。或「扔掉」;爲什麼在catch塊中「throw」和「throw ex」的行爲方式相同?

來源:http://msdn.microsoft.com/en-us/library/ms182363%28VS.80%29.aspx

「爲了跟上異常的原始堆棧跟蹤信息,使用throw語句不指定例外。」

但是當我嘗試這與

 try{ 
      try{ 
       try{ 
        throw new Exception("test"); // 13 
       }catch (Exception ex1){ 
        Console.WriteLine(ex1.ToString()); 
        throw; // 16 
       } 
      }catch (Exception ex2){ 
       Console.WriteLine(ex2.ToString()); // expected same stack trace 
       throw ex2; // 20 
      } 
     }catch (Exception ex3){ 
      Console.WriteLine(ex3.ToString()); 
     } 

我得到三個不同的堆棧。我期待第一和第二條軌跡是相同的。我究竟做錯了什麼?在ConsoleApplication1.Program測試 :(或理解錯誤?)

System.Exception的:在C在ConsoleApplication1.Program.Main測試 (字串[] args):\的Program.cs:行13 System.Exception的。主(字串[] args)在C:\的Program.cs:行16 System.Exception的:測試 在ConsoleApplication1.Program.Main(字串[] args)在C:\的Program.cs:線20

+0

是的,再次閱讀聲明。保持堆棧跟蹤做'扔'不'拋出EX2' –

+0

民俗,請閱讀實際的代碼。在一種情況下,OP使用「throw」,而在另一種情況下,他們使用「throw ex」。然而,在所有*三個例子中(不僅僅是'throw ex'的情況),有三種不同的堆棧(據推測)。如果堆棧軌跡真的不同,那麼這確實是一個有趣的問題。 – siride

+1

@mitch如果你閱讀這個問題,文檔所說的和我觀察到的是不同的!這是我想澄清的。爲什麼ex1和ex2疊加不同,如果我用「throw」重新拋出? – cquezel

回答

7

throw只會保留堆棧框架,如果你不從當前的一個扔掉它。通過以一種方法完成所有這些,您就是這麼做的。

看到這個答案:https://stackoverflow.com/a/5154318/1517578

PS:+1詢問這實際上是一個有效的問題的問題。

2

西蒙打我回答,但你可以從另一個函數內部拋出原始異常只看到預期的行爲:

static void Main(string[] args) 
{ 
    try 
    { 
     try 
     { 
      try 
      { 
       Foo(); 
      } 
      catch (Exception ex1) 
      { 
       Console.WriteLine(ex1.ToString()); 
       throw; 
      } 
     } 
     catch (Exception ex2) 
     { 
      Console.WriteLine(ex2.ToString()); // expected same stack trace 
      throw ex2; 
     } 
    } 
    catch (Exception ex3) 
    { 
     Console.WriteLine(ex3.ToString()); 
    } 
} 

static void Foo() 
{ 
    throw new Exception("Test2"); 
} 
+0

謝謝!但你有沒有注意到你的堆棧內容?第1行被保留,但第2行不同(在第一個和第二個堆棧跟蹤中)。我也沒有預料到這一點。 – cquezel

+0

我想這可以回溯到西蒙的回答。但是我認爲這對調試更好,因爲您知道在異常實際上成爲問題之前正在執行的代碼中的最後一個地方。 –

2

嗯,我挖了一點在這個問題上,這裏是我的非常個人化結論:

千萬不要使用「throw」,但總是重新拋出一個新的異常,並指定 原因。

這裏是我的推理:

我被我以前與Java經驗的影響和預期的C#拋非常相似的Java。嗯,我挖了一點在這個問題上,這裏是我的意見:

static void Main(string[] args){ 
     try { 
      try { 
       throw new Exception("test"); // 13 
      } 
      catch (Exception ex) { 
       Console.WriteLine(ex.ToString()); 
       throw ex;// 17 
      } 
     } catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
     } 
    } 

產量:

System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13 
System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17 

直觀地說,一個Java程序員本來期望這兩個例外是相同的:

System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13 

但是C#文檔清楚地表明這是預期的:

如果一個異常被指定在throw語句中的異常重新拋出,堆棧跟蹤在當前方法重新啓動,並拋出異常和當前的方法失去了原有的方法之間的調用列表的方式。要保持原始堆棧跟蹤信息的例外情況,請使用throw語句,而不指定異常。「

現在,如果我稍微更改了測試(替換throw ex;通過throw;在第17行)。

 try { 
      try { 
       throw new Exception("test"); // 13 
      } 
      catch (Exception ex) { 
       Console.WriteLine(ex.ToString()); 
       throw;// 17 
      } 
     } catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
     } 

產量:

System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13 
System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17 

顯然,這不是我所期望的(因爲這是原來的問題)。我在第二個堆棧跟蹤中丟失了原始的丟棄點。西蒙懷特黑德將解釋說明了這一點,只有當前方法中沒有發生異常時才保留堆棧跟蹤。所以在同樣的方法中「拋出」沒有參數是非常沒用的,因爲一般來說,它不會幫助你找到異常的原因。

throw new Exception("rethrow", ex);// 17 

產量:

做什麼任何Java程序員會做,我換成17行的聲明

System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13 

System.Exception: rethrow ---> System.Exception: test 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13 
    --- End of inner exception stack trace --- 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17 

這是一個更好的結果。

然後,我開始用方法調用進行測試。

private static void throwIt() { 
     throw new Exception("Test"); // 10 
    } 

    private static void rethrow(){ 
     try{ 
      throwIt(); // 15 
     } catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
      throw; // 18 
     } 
    } 

    static void Main(string[] args){ 
     try{ 
      rethrow(); // 24 
     } catch (Exception ex) { 
      Console.WriteLine(ex.ToString()); 
     } 
    } 

同樣,堆棧跟蹤不是我(Java程序員)所期望的。在Java中,這兩個堆棧都是相同的,並且深度爲三種:

java.lang.Exception: Test 
    at com.example.Test.throwIt(Test.java:10) 
    at com.example.Test.rethrow(Test.java:15) 
    at com.example.Test.main(Test.java:24) 

第一個堆棧跟蹤只有兩個方法深。

System.Exception: Test 
    at ConsoleApplication1.Program.throwIt() in Program.cs:line 10 
    at ConsoleApplication1.Program.rethrow() in Program.cs:line 15 

這就好像是堆棧跟蹤被填充爲堆棧展開過程的一部分。如果我要記錄堆棧跟蹤以調查此時的異常情況,我可能會丟失重要信息。

第二個堆棧跟蹤是三個深度方法,但是第18行(throw;)出現在其中。

System.Exception: Test 
    at ConsoleApplication1.Program.throwIt() in Program.cs:line 10 
    at ConsoleApplication1.Program.rethrow() in Program.cs:line 18 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 24 

這一觀察類似於剛纔的一個:不保存當前方法範圍堆棧跟蹤,並再次我鬆,其中所述異常發生時調用的方法。例如,如果rethow被寫成:

private static void rethrow(){ 
    try{ 
     if (test) 
      throwIt(); // 15 
     else 
      throwIt(); // 17 
    } catch (Exception ex) { 
     Console.WriteLine(ex.ToString()); 
     throw; // 20 
    } 
} 

息率

System.Exception: Test 
    at ConsoleApplication1.Program.throwIt() in Program.cs:line 10 
    at ConsoleApplication1.Program.rethrow() in Program.cs:line 20 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26 

()拋出異常哪個打電話throwIt? Stack說第20行,15行或17行也是如此?

的解決方案是一樣的前面:包裝事業在新的異常,其產生:

System.Exception: rethrow ---> System.Exception: Test 
    at ConsoleApplication1.Program.throwIt() in Program.cs:line 10 
    at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 17 
    --- End of inner exception stack trace --- 
    at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 20 
    at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26 

我簡單的總結,這一切是永遠不會使用「拋」;但始終重新拋出新例外與指定的原因。

相關問題