Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
本書第五章開始討論例外處理,例外處理是一件很容易被開發者忽略的事(尤其是一人專案)。在多人開發或是需要開放 API 給他人呼叫時,例外處理是必須要被好好處理的。
在程式發生未符預期情況時(或未符合條件)擲出例外,能夠讓錯誤早點浮上檯面,避免影響到後續流程與資料正確性;實務上有時會發生開發者為了圖一時方便,直接在 catch 區塊不做任何處置,造成後續追蹤錯誤困難。然而,過猶不及,也並非所有情況都必須強制擲出例外。比如說,File.Exists 當檔案不存在時回傳 false,而非跳出例外,因為檔案不存在這件事是方法指定要檢查的;檔案存在與否都在預期的情況內。但 File.Open 則會在檔案不存在時擲出例外,因為方法名稱已經很清楚的告訴呼叫端,就是要對檔案做讀取,而檔案不存在是不在預設的情境內,故擲出例外。
由此可知,擲出例外與否和方法命名與實際意圖有絕對關係。
一般的準則是:
下面來看一個例子。
var worker = new DoesWorkThatMightFail();
try
{
worker.DoWork();
}
catch (WorkerException ex)
{
Debug.WriteLine("WorkerException occurs");
}
此寫法表示預期 DoWork 有可能擲出例外,然而某些情況能夠允許此錯誤發生(流程上可以接受復歸)。當有這個情況時,利用擲出例外控制流程就不是一個好選擇。
改寫程式碼:
public class DoesWorkThatMightFail
{
public bool TryDoWork()
{
if(!TestConditions())
return false;
Work(); // 出錯時會擲出例外
return true;
}
private bool TestConditions()
{
// 省略程式碼
// 檢查是否符合 Work 條件
return true;
}
public void DoWork()
{
Work(); // 執行指定工作
}
private void Work()
{
// 省略
// 出錯時擲出例外
}
}
客戶端程式碼:
if(!worker.TryDoWork())
Debug.WriteLine("WorkerException occurs");
DoesWorkThatMightFail 提供了 TryDoWork 公有方法,並在類別內提供 TestConditions 私有方法。早期的檢查方法可用性並回傳布林值,作為流程控制的依據。(註:Work 方法自身也有可能擲出例外,此時布林值涵蓋範圍要看實際情況與設計而定。)
1. 利用例外處理讓 api 更明確表達意圖,早期發現錯誤早期提示。
2. 例外處理不要用來做流程控制,利用回傳布林值檢查程式狀態並另做處理。