凡事都有例外,寫程式也是如此。處理Exception可以說是最麻煩,最考驗軟體工程師的地方了。它可以很簡單的處理,也可以用很漂亮的方法來包裝:這真的就是技巧了。
但在那之前,我們先討論一件事:什麼是Exception?
通常而言,如果是和本身資源無關,系統雖然糟遇重大困難但如果適度的處理仍能繼續的事件,就叫例外。最常見的像陣列讀超過數量的IndexOutOfRangeException:這類的事件通常都可以預防,最差最差狀況就是用try-catch來補捉,沒什麼了不起的學問。
但如果發生了重大的事件是在建立本身的Platform上,系統無法補捉或必需用非常手段來處理的,那就是錯誤了,例如像OutOfMemoryError, StackOverflowError。
這個在Java有很嚴格的區別,但在C#就全部都叫Exception了(我就懶)。
對於這個問題後面有其他的討論。
所以根據上面的說明可以知道,原則上如果系統是安放在platform上,理論上Error不太容易觸碰到,除非寫了unsafe code,系統往底層寫,同時為了安全做了一些底層等級的處理。無論是那一種,它的命名原則都是:在字尾加上Exception,例如
class IllegalArgumentsException : Exception
在使用時,也是用camelCase
try
{
}
catch (OutOfOrderException outOfOrderException)
{
//do something
}
catch (Exception exception)
{
//do something
}
finally
{
}
如果
即然try catch那麼方便,那有什麼好講的?
我引述一段在網路上看到的回文:
Handling a stack overflow is not the right solution, instead, you must ensure that your program does not overflow the stack.
就像前面提到的"陣列讀超過數量的IndexOutOfRangeException",處理Exception最好的方法,應該是在它掉進catch section之前,就把可能發生的情況都處理掉,讓程式不會發生這類的Exception。或許IndexOutOfRangeException是一個非常初階的錯誤,但其實80%的Exception都是可以被處理掉的,在SDK文件裡也可以看到相關的方法會因為什麼情況拋出Exception。
所以在使用這類的API時,除了知道用法外,也應該要從Exception這節,知道一些相關使用的限制。
這是一個很常見的問題:為了不要讓程式出錯,用空的catch把Excption吞掉。
就像上面說的,Exception應該是軟體工程師一定要處理的問題,就算在共識上不處理,也應該要做三件事
如果程式補捉到了Exception,理想的處理方法應該是在當下處理掉:寫Log、排除、重試都是很適合的做法,但不要原封不動的把錯誤又拋到上一層去:連資料庫失敗,把同樣的Exception拋到上層並不能解決問題。如果真的要再拋出例外,應該把相關的例外增加更多的訊息,讓接收例外的程式知道發生的原因。
大多數的情況下,應該都可以補捉到大於一個的Exception,它們可能歸類在不同類別下。就像前面提到的,在SDK裡可以查到相關方法會拋出的例外,所以在程式裡也應該處理可能無法排除的例外。
那在排列例外補捉時,要把最常發生的例外方在最上層,然後依序往下到最後一個是General Exception,就是Exception它本人,也在這裡做最一般的處理法。
有些工程師喜歡用例外當成流程控制的一部份,但我個人認為並不是個很好的做法:例外還是應該保留給無法預期的情況。但如果整個流程裡沒有任何地方可以把錯誤告訴呼叫者,那例外就是唯一的方法告訴呼叫者應該修改相關的行為。但try-catch在效能上或多或少會有些影響,所以權衡之間還是要考量一下是否值得這樣做。
例外處理其實還滿多東西可以講的,前輩泰迪也把他的大作:《笑談軟體工程:例外處理設計的逆襲》公開在網路上下載,有興趣的不妨下載研究看看。