這個做法說明了 Exception Filters 與 when 子句的使用方式,還有與舊的判斷語句寫法進行比較。
例如這段程式碼會嘗試發送網路請求,並且捕捉 TimeoutException
但如果今天想要先判斷程式運行的狀態就必須透過在 catch 區塊中添加判斷語句,
進行分析,所以這段程式會不斷運行迴圈直到 retryCount 到達三個時才放棄請求,最後把錯誤進行拋出。
var retryCount = 0;
var dataString = default(String);
while (dataString == null)
{
try
{
dataString = MakeWebRequest();
}
catch (TimeoutException e)
{
if (retryCount++ < 3)
{
WriteLine("Timed out. Trying again");
// pause before trying again.
Task.Delay(1000 * retryCount);
}
else
throw;
}
}
這裡要特別注意 else 裡面的 throw
這個寫法是正確的,它會向外拋出原始錯誤並保留原始的 stack trace,不要寫成 throw e
這種寫法
雖然會拋出拋出原始錯誤但會把 stack trace 清除導致資訊丟失,更不要寫成 throw new Exception
這會建立新的 Exception
;
接下來可以使用 when
子句達到同樣的效果並且更容易閱讀,注意到程式會先判斷 when
的條件是否達成才會執行內部邏輯,
並且內部並沒有寫到 throw
將錯誤進行拋出,這是因為它找不到符合的 catch 內部會往 call stack 上層進行尋找。
var retryCount = 0;
var dataString = default(String);
while (dataString == null)
{
try
{
dataString = MakeWebRequest();
}
catch (TimeoutException e) when (retryCount++ < 3)
{
WriteLine("Operation timed out. Trying again");
// pause before trying again.
Task.Delay(1000 * retryCount);
}
}
另外實際上的錯誤發生地點也會有些微差異,例如第一種寫法會在報告中看到發生地點是在 throw
那行造成的,但如果改用 Exception Filters
的寫法則會在報告中看到發生地點是在 SingleBadThing
也就是實際發生問題的地點,這個微小差距在大型的程式中帶來非常有用的幫助,
並且 .NET CLR 會對有 when 子句的 try/catch 區塊進行優化並且提升運作的效率。
static void TreeOfErrors()
{
try
{
SingleBadThing();
}
catch (RecoverableException e)
{
throw; // reported on Call Stack
}
}
static void TreeOfErrorsTwo()
{
try
{
SingleBadThing(); // reported on Call Stack
}
catch (RecoverableException e) when (false)
{
WriteLine("Can't happen");
}
}
也就是說我們應該徹底改變之前的使用習慣,改成用 Exception Filters
是更好的選擇,例如說 HTTPException
它的 GetHttpCode 方法
會回傳 Http Response Code 表示請求的結果,像是可以在 when 子句判斷 code 是否為 404 之後在進行相對應的處理。
Summary
使用 Exception Filters 能夠把原本傳統判斷寫法大幅省略,只需要留下 catch 與 when 進行檢查,所以跟傳統的拋出處理方式這樣做 又可以把原始的錯誤訊息保留下來也可能讓程式運作得更快一點。