throw語句
在學習如何處理異常之前,我們先介紹一下throw語句。
throw語句拋出一個異常:
throw expression
帶有表達式的throw語句拋出的異常是在計算這個表達式時產生的。這個表達式必須表示一個System.Exception類型或它的派生類型的值。如果對表達式的計算產生的結果是null,則拋出的將是一個NullReferenceException異常。
不帶表達式的throw語句我們稍後再介紹。
異常處理語句
異常是由try語句來處理的。
try語句提供了一種機制來捕捉塊執行過程中發生的異常。以下是它的三種可能的形式:
●try-catch(s)
●try-finally
●try-catch(s)-finally
在介紹try語句之前,我們先介紹一個重要的概念:異常傳播。當一個異常被拋出以後,程序將控制權轉移給try語句中第一個能夠處理該異常的catch子句。這個從異常拋出到控制轉移給合適的異常處理語句的過程就叫做異常傳播。本文發表於www.bianceng.cn(編程入門網)
異常傳播包括重復執行以下步驟,直到找到一個與該異常相匹配的catch子句。
(1).由裡層到外層的執行每一個包圍拋出點(異常被拋出的最初位置)的try語句。
●如果當前的try塊包含一或多條catch子句,那麼將按照這些子句的出現順序對它們逐一檢查以定位適合該異常的處理。所謂適合該異常的處理也就是第一個定義了該異常類型或其基類型的catch語句,異常傳播也就結束了,程序控制轉移到該catch語句執行。
●否則,如果當前的try塊含有finally塊的話,就執行finally塊。如果該塊又拋出一個異常,則當前處理的異常被終止。如果沒有,則當finally塊執行完成以後,再繼續異常的處理。
(2).如果當前的成員函數調用中沒有定位異常處理,則調用終止。並且在該成員函數調用點將該異常拋給調用者,重復執行上一步。
(3).如果該異常終止了當前線程或進程的所有成員函數調用,則說明該線程或進程中不存在對異常的處理,它將自行終止。
下面讓我們回到try語句
如果catch語句中指定了一個類類型,則它必須是System.Exception類型或它的派生類型。如果同時指定了類類型和標識符,就是聲明了一個異常變量。異常變量相當於一個作用范圍為整個catch塊的局部變量。在catch塊的執行過程中,異常變量描述了當前正在處理的異常。如果想引用異常對象(其中包括很多重要的錯誤信息),就必須定義異常變量。
在一個catch塊中,可以用不含表達式的throw語句將該catch塊捕捉到的異常再次拋出,對於異常變量的分配不會改變兩次拋出的異常。
既沒有定義異常類型也沒有定義異常變量的catch子句稱做一般catch子句。該catch子句一般在事先不能確定會發生什麼樣的異常的情況下使用。一個try語句中只能有一個一般catch子句,而且如果有的話,該catch子句必須排在其它catch子句的後面。
盡管throw語句(含表達式)只能拋出System.Exception類型或它的派生類型的異常,但其它語句並不受該規則限制,因而可以拋出其它類型的異常。比如用一般catch語句就可以捕捉這一類異常,然後利用一個不含表達式的throw語句就可將其再次拋出。
由於尋求異常的處理是通過按照catch子句出現的順序逐一檢查catch子句,因此如果有一個catch子句定義的異常類型與比它先出現的catch子句定義的類型相同或是它的派生類型,就會發生錯誤。下面的例子中我們演示了try-catch語句的使用以及再次拋出異常。
程序清單8-9:
using System; class Test { static void F(){ try{ G(); } catch(Exception e){ Console.WriteLine("Exception in F:"+e.Message); e=new Exception("F"); throw; } } static void G(){ throw new Exception("G"); } static void Main(){ try{ F(); } catch(Exception){ Console.WriteLine("Exception in Main:"+e.Message); } } }
F方法捕捉到了一個異常,向控制台寫了一些診斷信息,改變異常變量的內容,然後將該異常再次拋出。這個被再次拋出的異常與最初捕捉到的異常是同一個。因此程序的輸出結果如下:
Exception in F:G
Exception in Main:G
當try語句執行完成後,finally塊中的語句必將被執行。不論是否會發生由以下原因導致的程序控制轉移:
●普通操作的結果;
●執行break,continue,goto,或return語句的結果;
●將異常傳播到語句之外的結果。
我們用一個例子來證明finally語句的運行。
程序清單8-10:
using System; class Test { public static void Main() { try { Console.WriteLine("try"); goto leave; } finally { Console.WriteLine("finally"); } leave: Console.WriteLine("leave"); } }
該程序的輸出結果為:
try
finally
leave
由此可見,finally子句總能被執行。因此我們可以利用try-finally來清除異常。
如果在執行finally塊時拋出了一個異常,這個異常將被傳播到下一輪try語句中去。如果在異常傳播過程中又發生了一個異常,那麼這個異常將被丟失。
最後,由於我們對待異常的態度往往是:捕捉、清除、繼續執行程序,因此我們需要在程序中使用try-catch(s)-finally結構。
下面的例子計算函數值
float x,y,z; try{ z=Math.Sqrt(x*x-y*y); } catch{ z=Math.Sqrt(y*y-x*x); } finally{ z=z+x; }
其中第一個try語句捕捉負數開根號的異常,並與第二個catch語句配合達到取絕對值的目的。