try語句提供了一種捕獲程序發生異常的機制。try語句有三種形式:
1、一個try塊兒后跟一個或多個catch塊兒
2、一個try塊兒后跟一個finally塊兒
3、一個try塊兒后跟一個或多個catch塊兒,最后再跟一個finally塊兒。(最常用)
例子:聲明一個Caculator類型,並定義一個Add()方法,調用這個Add()方法並傳入該方法需要的兩個string類型的參數,即可求出兩數之和。
class Program { static void Main(string[] args) { Caculator caculator = new Caculator(); int result= caculator.Add("abc","123"); Console.WriteLine(result); } } class Caculator { public int Add(string arg1,string arg2) { int a = int.Parse(arg1); int b = int.Parse(arg2); return a + b; } }
1):執行上面的代碼程序會出現下面的錯誤信息。
Unhandled exception. System.FormatException: Input string was not in a correct format. at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type) at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info) at System.Int32.Parse(String s) at trycatchstatement.Caculator.Add(String arg1, String arg2) in.....
這並不是我們預期想要的結果。在上述信息中可以看到,我們要認識的第一個異常:格式化異常(System.FormatException);冒號后面是對異常的描述:輸入的字符串不是正確的格式。
2):為了規避程序發生的格式化異常的錯誤,我們使用try..catch來改寫代碼。
在使用try語句的時候要注意,哪里出錯就將哪里的代碼包括進try塊兒中。這里我們很容易能夠判斷出來:int的Parse方法會出現FormatException。
public int Add(string arg1, string arg2) { int a = 0; int b = 0; try { a = int.Parse(arg1); b = int.Parse(arg2); } catch { Console.WriteLine("參數輸入有誤"); } return a + b; }
再次運行程序:
參數輸入有誤 0
分析:由於參數arg1是一個"abc"值,所以代碼在執行到a=int.Parse(arg1)這一行時會產生FormatException;但是由於這段代碼包含在了try塊兒中,所以當異常發生時程序就會進入到catch塊兒中進行處理。這里我們的處理,就是打印在控制台中一行自己定義的錯誤提示。
3):上面的例子中,catch后面什么也沒寫,這種屬於捕捉通用類型的異常;但catch后面其實可以跟一個錯誤類型,來捕捉特定類型的異常。
由於我們從前面控制台中輸出的異常信息中,可以得知我們的代碼會發生FormatException。所以我們可以這樣改寫程序。
public int Add(string arg1, string arg2) { int a = 0; int b = 0; try { a = int.Parse(arg1); b = int.Parse(arg2); } catch(FormatException) { Console.WriteLine("發生了類型轉換異常"); } return a + b; }
運行結果:
發生了類型轉換異常 0
4):其實到此為止,我們的程序並不是完全沒有問題的;比如程序調用時這樣寫:
static void Main(string[] args) { Caculator caculator = new Caculator(); int result = caculator.Add("99999999999999999", "123"); Console.WriteLine(result); }
運行結果:
Unhandled exception. System.OverflowException: Value was either too large or too small for an Int32. at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type) at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info) at System.Int32.Parse(String s) at trycatchstatement.Caculator.Add(String arg1, String arg2) in....
分析:從控制台輸出的錯誤信息可以知道我們又遇到了一種新的異常--溢出異常(System.OverflowException);異常詳細信息是:對於一個Int32類型的變量來說值過大或者過小。而觀察我們傳入的999..999可以知道我們傳入的值如果轉換成數值就會超出int類型能夠容納的范圍,所以我們還要追加catch塊兒,來把OverflowException考慮進去。
public int Add(string arg1, string arg2) { int a = 0; int b = 0; try { a = int.Parse(arg1); b = int.Parse(arg2); } catch(FormatException) { Console.WriteLine("發生了類型轉換異常"); } catch (OverflowException) { Console.WriteLine("發生了溢出異常"); } return a + b; }
運行結果:
發生了溢出異常 0
分析:上面的代碼其實是為了練習try塊兒后面跟多個catch塊兒的情景。其實我們沒有把可能發生的異常全部考慮進去,只是達到了聯系效果。
5):其實在catch后面除了跟異常的類型外,還可以在類型后面跟異常的變量名,目的是當捕捉到異常的時候,可以使用異常變量所包含的信息。
class Program { static void Main(string[] args) { Caculator caculator = new Caculator(); int result = caculator.Add(null, "123"); Console.WriteLine(result); } } class Caculator { public int Add(string arg1, string arg2) { int a = 0; int b = 0; try { a = int.Parse(arg1); b = int.Parse(arg2); } catch(FormatException fe) { Console.WriteLine(fe.Message); } catch (OverflowException oe) { Console.WriteLine(oe.Message); } catch (ArgumentNullException ae) { Console.WriteLine(ae.Message); } return a + b; } }
運行結果:
Value cannot be null. (Parameter 's') 0
6):至此我們還沒用到finally。當程序順利離開try語句時,如果我們寫的有finally塊兒,那么finally塊兒是一定會執行的。
舉例:模擬一個在try語句中使用了return語句的操作,(一般情況下我們會認為程序執行到return語句時就會結束),但是從下面代碼的輸出可以觀察到finally塊兒還是會被執行的。
public int Add(string arg1, string arg2) { bool hasError = false; int a = 0; int b = 0; try { a = int.Parse(arg1); b = int.Parse(arg2); return a + b; } catch (FormatException fe) { Console.WriteLine(fe.Message); hasError = true; return -1; } catch (OverflowException oe) { Console.WriteLine(oe.Message); hasError = true; return -1; } catch (ArgumentNullException ae) { Console.WriteLine(ae.Message); hasError = true; return -1; } finally { if (hasError) { Console.WriteLine("發生了異常"); } else { Console.WriteLine("執行結束"); } } }
運行結果:
Value cannot be null. (Parameter 's') 發生了異常 -1
分析:上面程序實現的邏輯是:當程序發生異常時,由對應的catch塊兒進行處理后 return一個-1。然后再在finally中進行一個程序是否有錯誤發生的判斷進而向用戶顯示程序執行后得的結果是否是正常合理的值。從上面的輸出可以看出即使在程序發生了異常,返回了-1的情況下,finally塊兒仍然執行了。
例子:continue語句,能阻止finally塊兒執行么?(下面程序中的CountNumber方法的目的是統計數組中符合條件的元素有幾個)
class Program { static void Main(string[] args) { Caculator caculator = new Caculator(); //int result = caculator.Add(null, "123"); //Console.WriteLine(result); string[] arr = { "999", "-1", "123", "12.3","-0.2" }; int r= caculator.CountNumber(arr); Console.WriteLine(r); } } class Caculator { public int CountNumber(string[] arr) { int result = 0; foreach (var item in arr) { try { int i = int.Parse(item); if (i < 0) continue; else result++; } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Console.WriteLine($"{item}"); } } return result; } }
運行結果:
999 -1 123 Input string was not in a correct format. 12.3 Input string was not in a correct format. -0.2 2
分析:可以看出程序進入了catch塊兒或者執行了continue語句,不能阻止inally塊兒執行。
下面是程序執行時的調試過程:
III:將異常傳播到try語句之外。
如何將異常傳播到try語句之外呢?我們需要借助throw關鍵字。程序修改如下。
class Program { static void Main(string[] args) { Caculator caculator = new Caculator(); //int result = caculator.Add(null, "123"); //Console.WriteLine(result); try { string[] arr = { "999", "-1", "123", "12.3", "-0.2" }; int r = caculator.CountNumber(arr); Console.WriteLine(r); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } class Caculator { public int CountNumber(string[] arr) { int result = 0; foreach (var item in arr) { try { int i = int.Parse(item); if (i < 0) continue; else result++; } catch (Exception ex) { throw ex; } finally { Console.WriteLine($"{item}"); } } return result; } }
運行結果:
999 -1 123 12.3 Input string was not in a correct format.
分析:當我們通過throw將異常傳播到try之外后, 那么異常該怎么處理呢?誰調用誰處理。所以我們在Main()方法中,調用caculator.CountNumber(arr)的位置使用try語句包括起來,這樣程序就不會報錯了。此外,可以觀察到12.3還是被輸出了--這表明了在CountNumber方法中finally塊兒還是被執行了。下圖是執行過程。
總結:
1、在catch塊兒中,我們經常用Exception類型進行異常的捕獲,它是一個通用的異常類,各種異常都會被捕捉到;
2、finally塊兒在上面我們講到的情況下都會被執行,使用finally塊兒通常是為了執行一些必須要執行的操作:比如釋放一些被Lock的資源、比如雖然程序表面上沒有因異常而死掉但確實發生了異常時需要給用戶有好的提示等;
3、我們在寫程序的時候要盡可能多地在可能出現異常的地方去使用try...catch語句去捕捉異常,以防異常沒有被捕捉到引起程序崩潰,被測試小姐姐給自己提BUG。
以上便是try語句的學習總結,記錄下來以便以后查閱。