c#
throw exception
正確重拋例外 (Exception) 的方式
2018/10/17 15:04:33
0
2253
正確重拋例外 (Exception) 的方式
簡介 |
本文說明如何正確重拋例外 (Exception),避免重要資訊被錯誤的重拋方式給隱藏。 |
作者 |
陳志澤 |
前言
在 Review 專案程式碼時,常會發現有把 Try-Catch 捕捉到之 Exception 直接拋出的狀況 (如左下圖所示),其實這樣是非常不恰當的做法;較好的方式是使用 throw 重拋例外就好了 (如右下圖所示)。這兩者到底由何差異,以下將就簡單實例來進行探討。
在 Review 專案程式碼時,常會發現有把 Try-Catch 捕捉到之 Exception 直接拋出的狀況 (如左下圖所示),其實這樣是非常不恰當的做法;較好的方式是使用 throw 重拋例外就好了 (如右下圖所示)。這兩者到底由何差異,以下將就簡單實例來進行探討。
![](/tpu/File/html/201810/20181012144651/images/20181012124755.png)
差異比較
當例外被在捕捉時,可從 Exception.StackTrace 屬性中來獲得呼叫堆疊資訊,進而了解錯誤發生的確切位置。以下將就該資訊的完整度探討兩者之差異。
首先,先建立一個呼叫的推疊類別做測試。當呼叫 Aoo.DoSomethingInBoo() 方法時,會依序向 Boo, Coo, Doo 的 DoSomethingInXoo() 方法執行,且最後在 Doo.DoSomethingInDoo() 中故意造成錯誤,再交由 Boo.DoSomethingInBoo() 捕捉此例外錯誤,並以 throw ex 或 throw 拋出例外至呼叫端中來比較差異。
呼叫端測試主程式如下,捕捉到例外錯誤後印出 StackTrace 資訊於畫面上。
使用 throw ex 拋出
如下圖所示,先使用 throw ex 來拋出例外,執行看看。
![](/tpu/File/html/201810/20181012144651/images/20181012125019.png)
呼叫端測試主程式如下,捕捉到例外錯誤後印出 StackTrace 資訊於畫面上。
![](/tpu/File/html/201810/20181012144651/images/20181012135455.png)
使用 throw ex 拋出
如下圖所示,先使用 throw ex 來拋出例外,執行看看。
![](/tpu/File/html/201810/20181012144651/images/20181012135728.png)
當我們使用 throw ex 時,會重置呼叫堆疊,造成確切例外發生位置的遺失。如下圖所示,錯誤發生點明明就在 Doo.DoSomethingInDoo() 中,而 StackTrace 提供的資料卻只有到 Boo.DoSomethingInBoo(),因此單就此訊息無法得知實際錯誤到底是在哪個類別方法中發生的,實在是會造成很大的困擾。
![](/tpu/File/html/201810/20181012144651/images/20181012135754.png)
使用 throw 拋出
接著使用 throw 來拋出例外,執行看看。
![](/tpu/File/html/201810/20181012144651/images/20181012135812.png)
使用 throw 拋出錯誤時,並不會重置呼叫堆疊,保有完整 StackTrace 資訊,方便獲得例外發生點來進行除錯。如下圖所示,明確地指出錯誤發生點就在 Doo.DoSomethingInDoo() 中,獲得完整呼叫堆疊資訊。
![](/tpu/File/html/201810/20181012144651/images/20181012135830.png)
測試代碼
以下是完整的測試代碼,有興趣的朋友可以自行測試
class Program
{ static void Main(string[] args) { try { Aoo aoo = new Aoo(); aoo.DoSomethingInAoo(); } catch (Exception ex) { // display stack trace info Console.WriteLine( "----- stack info -----" ); Console.WriteLine( ex.StackTrace.ToString() ); Console.WriteLine( "----------------------" ); } Console.Read(); } } // level 1 public class Aoo { public Boo boo = new Boo(); public void DoSomethingInAoo() { boo.DoSomethingInBoo(); } } // level 2 public class Boo { public Coo coo = new Coo(); public void DoSomethingInBoo() { try { coo.DoSomethingInCoo(); } catch (Exception ex) { // log here // ... // ## Rethrow exception ## // destroys the strack trace info! //throw ex; // ## Rethrow exception ## // preserves the stack trace throw; } } } // level 3 public class Coo { public Doo doo = new Doo(); public void DoSomethingInCoo() { doo.DoSomethingInDoo(); } } // level 4 public class Doo { public void DoSomethingInDoo() { int i = 0; i = 1 / i; // cause exception } } |