[C#] 自訂例外處理
Introduction
程式無法順利執行的原因很多,可能是資料問題、硬體問題、網路問題、記憶體不足或是檔案遺失等等問題,
也許在怎麼周密的程式,都難免周全,例外處理是一種機制,當例外發生時,我們可以發出必要訊息並處理
接下來後續的動作,比如說,資料庫的回傳數據不正確,造成程式錯誤,這時,程式接收到這個例外,也許,
會發個 e-mail 或是其他方式,通知管理員,並且還可以讓程式避免自動終止,或是將錯誤訊息直接 show 出。
常用的 try-catch 來幫助我們處理例外的發生。例外狀況很多,列出一些參考資料。
| 例外狀況 | 原因 | 
| ArgumentException | 當其中一個提供給方法的引數為無效時所擲回的例外狀況。 | 
| ArithmeticException | 為算術、轉型 (Casting) 或轉換作業中的錯誤擲回例外狀況。 | 
| DivideByZeroException | 嘗試將整數或小數值除以零時所擲回的例外狀況。 | 
| DllNotFoundException | DLL 匯入中所指定的 DLL 找不到時所擲回的例外狀況。 | 
| FormatException | 當引數的格式不符合叫用 (Invoke) 方法的參數規格時所擲回的例外狀況。 | 
| MissingFieldException | 當嘗試動態存取不存在的欄位時,所擲回的例外狀況。 | 
| OutOfMemoryException | 當沒有足夠的記憶體繼續執行程式時,所擲回的例外狀況。 | 
| OverflowException | 當檢查內容中的算數、轉型 (Casting) 或轉換作業發生溢位時所擲回的例外狀況。 | 
| NullReferenceException | 當嘗試解除 Null 物件的參考時,所擲回的例外狀況。 | 
| IndexOutOfRangeException | 嘗試使用陣列以外的索引來存取陣列的元素時所擲回的例外狀況。這個類別無法被繼承。 | 
這邊只是冰山一角,請參考 MSDN 。
效能問題
我們必須透過設計,讓擲回例外狀況可能會對效能產生負面的影響。
以下範例參考 http://msdn.microsoft.com/zh-tw/library/ms229009.aspx
ex1
此範例包含一個方法,這個方法需要傳入一個字串型別的參數,若此方法傳遞 null 值給它時,
擲回例外狀況。如果經常呼叫此方法,它可能會對效能產生負面影響。
public class Doer
{
    // Method that can potential throw exceptions often.
    public static void ProcessMessage(string message)
    {
        if (message == null)
        {
            throw new ArgumentNullException("message");
        }
    }
    // Other methods...
}
ex2
修正範例1,判斷傳進來的參數是否為 null 值。
public class Doer
{
    // Method that can potential throw exceptions often.
    public static void ProcessMessage(string message)
    {
        if (message != null)
        {
            ProcessMessage("message");
        }
    }  
}
當然,我還是覺得 method 可以加入註解說明,提醒要調用這個方法的使用者,說明傳入參數不可為 null 值,
若為 null 則丟出錯誤訊息。或是,不處理為 null 值的參數。
自訂例外類別
自訂例外狀況的標準做法,可以參考 設計自訂例外狀況。
class MyException : Exception, ISerializable
{
    public MyException()
        : base("show message") { }
    public MyException(string message) 
        : base(message) { }
    public MyException(string message, Exception inner) 
        : base(message, inner) { }
    protected MyException(SerializationInfo info, StreamingContext context) 
        : base(info, context) { }
}
若要丟出例外:
throw new MyException();
Exception 還可以攜帶更多資訊,以下列出參考。(Exception 成員)
| 成員 | 類別 | 說明 | 
| Data | 屬性 | 取得由索引鍵/值組所組成的集合,提供關於此例外狀況的額外使用者定義資訊。 | 
| HelpLink | 屬性 | 取得或設定與這個例外狀況相關聯說明檔的連結。 | 
| InnerException | 屬性 | 取得造成目前例外狀況的 Exception 執行個體。 | 
| Source | 屬性 | 取得或設定造成錯誤的應用程式或物件的名稱。 | 
| Message | 屬性 | 取得描述目前例外狀況的訊息。 | 
| GetBaseException | 方法 | 在衍生類別中覆寫時,傳回一或多個後續的例外狀況的根本原因 Exception。 | 
| GetObjectData | 方法 | 在衍生類別中覆寫時,使用例外狀況的資訊設定 SerializationInfo。 | 
一個類別丟出例外一個類別捕捉例外
有時你未必知道使用者如何使用你的物件,有時候別人會以錯誤的方式來使用你的物件。
丟出例外的意義是:知道有甚麼事情可能發生,可以安排緊急的應變方式,而且例外通常是
由另一個物件丟出的。
TFileManager.cs
class TFileManager {
        public static void OpenFile(string Path) {
            try {
                System.IO.File.Open(Path, System.IO.FileMode.Open);
            }catch(System.IO.FileNotFoundException ex){
                throw ex;
            }
        }
    }
Program.cs
    class Program {
        static void Main(string[] args) {
            try {
                TFileManager.OpenFile("MyNote.txt");
            }
            catch (System.IO.FileNotFoundException) { 
                //...
                //建立一個 MyNote.txt 文件。
                //或是其他敘述。
                Console.WriteLine("throw Exception.");
            }
            Console.ReadKey();
        }
    }
輸出結果
注意
- 小心建構子裡的例外
 
當例外發生在建構子時,我們該如何處理。
參考下面例子當例外發生時,試圖實例化該類別的陳述式最後就不會得到一個實例。
try{ CurrentExcuse = new Excuse(name); }catch{ ... //這邊你可以重新實例化,或是其他敘述。 // CurrentExcuse = new Excuse(); ... }
Link
三小俠 小弟獻醜,歡迎指教