Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2311

實戰小技巧 - .NET Exception Message、InnerException 與 ToString()

$
0
0

前篇文章提到 try catch 時若只保留 Exception.Message,可能遺失 InnerException 及 StackTrace 錯失破案重要線索。文章迴響顯示這是個值得介紹的實戰技巧,故再補充一篇。

在某些應用情境我們會選擇使用 try … catch 達成特定目的,例如:(註:Exception 的官方翻譯為例外狀況,這裡容我以用較口語化的「錯誤」取代)

  1. 捕捉可預期錯誤,進行補救並繼續執行程式
    例如:發現作業失敗時,Rollback 交易、寫 Log、通知管理員、退回前一步驟請使用者再試一次... 比程式直接 Crash 來得好。
  2. 捕捉可預期錯誤,改顯示較易懂的錯誤訊息
    例如: 補捉 KeyNotFoundException 傳回錯誤訊息「系統資料未包含您指定的選項,請連絡客服人員」,會比「指定的索引鍵不在字典中」更容易理解。
  3. 捕捉錯誤後改抛回自訂錯誤型別
    優點是上層呼叫端可以使用 catch (MyCustomException mce) 針對自訂錯誤執行特定邏輯。
    而這裡有個小技巧,Exception 有個屬性 InnerException,補捉錯誤並拋出自訂錯誤時要記得將原始 Exception 放入自訂錯誤的 InnerException(稍後將有範例),以便呼叫端追查真實錯誤原因。

關於使用 try … catch 的正確姿勢,微軟文件庫有份文件:例外狀況的最佳作法 - Microsoft Docs可以參考。

下面的程式示範如何捕捉錯誤並改抛回自訂 MyCustException。建構 MyCustException 時要將捕捉到的 ArgumentException 當成 InnerException 包進物件一併傳到上層。而 Main() 在 try catch 時犯了一個錯,它只顯示 MyCustException.Message 就交差:

class Program
    {
staticvoid Main(string[] args)
        {
try
            {
                Test();
            }
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
 
staticvoid Test()
        {
try
            {
                InnerCall();
            }
catch (Exception ex)
            {
thrownew MyCustException(
"InnerCall出錯", 
                    ex);               
            }
        }
 
staticvoid InnerCall()
        {
thrownew ArgumentException("明知故犯");
        }
    }
 
class MyCustException : ApplicationException
    {
public MyCustException(string message,
            Exception innerException) :
base(message, innerException)
        {           
        }
    }

如下圖所示,執行時只會看到:

至於為什麼 InnerCall 出錯,錯在哪一段程式,鬼才知道?

要挖出錯誤根源,應檢查 ex.InnerException 是否不為 null,則有內部錯誤資訊,再由 ex.InnerException.Message 取出底層錯誤訊息,但要留意 ex.InnerException 可能還會有 InnerException,真正的錯誤訊息藏在 ex.InnerException.InnerException.Message。換句話說,得寫段遞迴一路剝洋葱才能 100% 保證挖出真正出錯原因。另外,想像 ASP.NET 出錯畫面(YSOD,Yellow Screen of Death)顯示程式碼錯在哪一行,則要透過 Exception.StackTrace取得。

聽起來很麻煩,但有條捷徑,將 ex.Message 改成 ex.ToString() 就好了!

如上圖所示,ToString() 會包含 InnerException (黃字部分),以及 StackTrace (方法名稱與程式行數),該有的資訊都有。

【結論】try catch 時要保存或顯示完整錯誤資訊,建議改用 ToString(),別只用 Message 讓真相消失在風中。


Viewing all articles
Browse latest Browse all 2311

Trending Articles