例外處理Exception (一)
例外處理Exception在程式語言中是比較進階的內容,而一個好的程式會善用例外處理來告訴使用者錯誤訊息,在程式寫好要上線時,就應該要做好例外處理,使用者才不會不知道哪裡操作錯誤。
例外處理的程式語法如下:
try { //程式在執行時可能發生錯誤的地方 }
catch (Exception ex) { //程式發生錯誤時會執行的區塊,通常是告知使用者操作錯誤}
finally { //不論程式是否發生錯誤都一定會執行的區塊 }
上面程式中的try…catch…是例外處理程式中一定要有的部分,try是一定會和catch一起出現的,而finally則是可有可無,舉例如下:
static void Main(string[] args)
{
int answer, a, b;
a = 20;
b = 0;
try
{
answer = a / b;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.ToString());
}
finally
{
Console.WriteLine("程式已結束");
}
Console.ReadKey();
}
程式輸出結果
嘗試以零除。 System.DivideByZeroException: 嘗試以零除。 於 MyTryCatchSample.Program.Main(String[] args) 於 E:\Program design\Book Demo\Visual C# 2017 gotop classical program design\Sample\ch08\ExceptionPractice\MyTryCatchSample\Program.cs: 行 20 程式已結束 |
在上面的程式中,因為除數為0,所以造成程式的錯誤。Exception是例外類別,例外類別有很多種,而Exception是最大的例外類別,可以捕捉到所有的例外,在Exception下面會有很多子類別。
上面的程式中ex.Message是內建的錯誤訊息,而如果使用ex.ToString()則是會印出程式的詳細錯誤訊息,在程式的哪一行執行時發生錯誤。最後finally是可以不用寫的,如果沒有finally那就是上面的程式輸出結果不會有最後一行。
使用catch抓錯誤時,要注意避免一開始就用Exception,應該是盡量要先使用Exception下面子類別,先去抓到子類別的錯誤,最後才是使用Exception,為什麼要這樣呢?那是因為Exception已經涵蓋了所有的錯誤,就變成只知道程式出錯卻不知道是屬於哪一項錯誤出錯,應該先抓到這是哪一個錯誤類別,然後再告訴使用者錯誤訊息。
舉例來說就好像今天去銀行領錢,結果銀行電腦大當機不能領錢,沒有跟你說到底為什麼不能領錢,只跟你說現在電腦大當機,如果銀行夠負責任,就要在調查出原因以後很快就要對外說明原因,要告訴大家是為什麼電腦會當機,並且告訴大家預計多久恢復正常。
另外要注意,如果一開始就先使用Exception的話,後面是不能再使用Exception下面子類別的,因為沒有意義。舉例來說,就好像今天有一個小孩在學校犯錯,老師直接告訴校長,校長和老師談完就已經處理完事情結束了,那這樣下面的主任還需不需要處理?不需要。如果今天這個小孩犯錯,老師是先跟主任談,因為這個錯很嚴重,沒辦法馬上就處理好,只好再往上呈報到校長那裡才處理完。Exception就是如此,使用Exception就已經會把所有的錯誤抓出來了,這樣事情就處理好了,既然錯誤已經抓出來了,那再使用下面的Exception子類別其實是沒有意義的。
舉個實際的範例來為大家說明。
static void Main(string[] args)
{
int answer, a, b;
a = 20;
try
{
b = Int32.Parse(Console.ReadLine());
answer = a / b;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("錯誤代碼168,除數不能為0");
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.ToString());
}
finally
{
Console.WriteLine("程式已結束");
}
Console.ReadKey();
}
在上面的程式中,DivideByZeroException就是Exception這一個例外類別下面的子類別,先從DivideByZeroException去抓到這個除數不得為0的錯誤,用自訂錯誤訊息告訴使用者「錯誤代碼168,除數不能為0」,後面才是Exception來抓到程式的其他錯誤。
本程式的除數是自行輸入的,若輸入0的話輸出結果如下:
0 錯誤代碼168,除數不能為0 嘗試以零除。 程式已結束 |
除了除數不能為0以外,如果輸入文字也是會造成程式錯誤的,必須要輸入數字算式才正確,在這個程式中,如果使用者輸入文字的話會是由最大的例外類別Exception抓到這個錯誤,輸出結果如下:
A 輸入字串格式不正確。 System.FormatException: 輸入字串格式不正確。 於 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) 於 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) 於 System.Int32.Parse(String s) 於 MyTryCatchSample.Program.Main(String[] args) 於 E:\Program design\Book Demo\Visual C# 2017 gotop classical program design\Sample\ch08\ExceptionPractice\MyTryCatchSample\Program.cs: 行 19 程式已結束 |
在實務上,程式設計師應該要設想到使用者可能會亂輸入所產生的錯誤,把使用者當白痴所有可能發生的狀況都想過,然後寫在catch裡面用自訂錯誤訊息告知使用者哪裡錯了。像上面的程式中就沒有用到Exception下面的子類別把不能輸入文字的錯誤給抓出來,所以其實還可以再改進,從上面程式最後的輸出結果知道這個錯誤屬於System.FormatException這個例外類別,所以我可以再用一個catch抓這個錯誤。
catch (FormatException ex)
{
Console.WriteLine("錯誤代碼88,請輸入數字");
}
這樣子如果使用者輸入文字,輸出結果就會像下面這樣:
A 錯誤代碼88,請輸入數字 程式已結束 |
像這樣子使用者才清楚是哪裏操作錯誤,在告知使用者錯誤訊息時,比較好的做法就是像我上面寫的程式一樣,要給使用者錯誤代碼和操作錯誤的文字訊息,然後使用者再從錯誤代碼去查這個錯誤代表的意義,這在我們平常上網開網頁時發生錯誤就會遇到,像是找不到網頁時瀏覽器會告訴使用者錯誤404,會這樣做是因為數字是全世界都通用的語言,大家都看得懂,看到數字以後再去查這個錯誤的意思就可以了。
另外要注意exception.Message這是不應該讓使用者看到的,這應該是只能讓程式設計師看到的,如果程式還在開發階段,把這些錯誤訊息顯示在畫面上還沒有甚麼問題,但是要注意如果是已經上線的系統就不應該讓使用者看到這些錯誤訊息了,根據我的工作經驗,有一個做法是把這些錯誤訊息輸出成一個log檔,程式開發人員去看log檔就好了。檔案處理的程式在這裡就不詳述了,有興趣的朋友可以上網查詢。
這一篇介紹了使用try…catch抓出錯誤,而例外處理其實還沒有結束,下一篇會介紹如何使用throw來自訂例外處理。