[C#.NET] 使用 try/catch 攔截例外應該注意事項
在上篇寫道 [.NET] 使用 using 或 try/finally 清理資源,養成使用try/finally或是using習慣來釋放資源才能確保動作正常,在主執行緒(UI)程式碼裡可能會用以下片斷程式碼
public static void ToXml(string FileName, object Object)
{
XmlSerializer xml = null;
Stream stream = null;
StreamWriter writer = null;
try
{
xml = new XmlSerializer(Object.GetType());
stream = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Read);
writer = new StreamWriter(stream, Encoding.UTF8);
xml.Serialize(writer, Object);
}
catch (Exception ex)
{
throw ex;
}
finally
{
writer.Close();
stream.Close();
}
}
但上述的寫法很習慣的用catch來補捉例外,但有很多不好的習慣,為什麼!?
1.因為不同的類別有不同的例外行為 XmlSerializer 有它自己的例外,FileStream 也是,我們應該為各個類別建立明確的例外。
2.所以把Exception當成是首要的攔截是不對的,應該攔截較明確的例外狀況,所以要把Exception以及SystemException的捕捉擺在最後面。請勿攔截一般例外狀況型別:因為攔截一般例外狀況類型會向程式庫使用者隱藏執行階段的問題,因而使偵錯變得更複雜。
3.Exception 是基底類別,其他的例外子類別都繼承於它們,子類別的訊息可能是父類別沒有的,所以用它們來捕捉例外,你可能會丟失很多東西。
分享一下我的做法,我一開始時(系統上線前)並不會寫catch區段的程式碼,當測試時發現了例外,如下圖:
我再為它捕上新的例外,當例外再度發生時,就會跑到該區塊
用 Exception 當下也可以捕捉到例外,這時再更改正確的例外。
剛剛那個方法是在MSDN上我在System.Xml.Serialization命名空間裡找不到相關的Exception類別,所以才出此下策,若在MSDN有相關的類別就比較容易處理。
再看看Stream類別的處理方式,Stream屬於System.IO命名空間,所以可以從這裡下手,我都是搜尋關鍵鍵字Exception,找到你認為可能發生的,就把它寫在catch區段,但並不是所有的例外都會寫出來,這時又得使用下策。
下圖就是發生FileNotFoundException例外
後記:
我在寫底層元件的時候,不寫任何的例外狀況,並確保資源有正確的被釋放,如下範例。例外全交由主執行緒也就是UI去處理,由它去捕捉例外並且統一記錄。
public static void ToXml(string FileName, object Object)
{
XmlSerializer xml = null;
Stream stream = null;
StreamWriter writer = null;
try
{
xml = new XmlSerializer(Object.GetType());
stream = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Read);
writer = new StreamWriter(stream, Encoding.UTF8);
xml.Serialize(writer, Object);
}
finally
{
if (writer != null)
writer.Close();
if (stream != null)
stream.Close();
}
}
有人會說這樣寫例外發生時不就消失了!?其實並不會消失,只要你沒寫catch區段,它會一輩子跟著你;假設我在用戶端程呼叫時,當例外發生時,會先執行ToXml方法的finally區段,然後才會執行用戶端的catch區段
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET