《C#與.NET3.5進階程式設計(第4版)》筆記7

來源:互聯網
上載者:User

第七章 結構化異常處理

本章主要介紹了使用結構化異常處理來處理C#代碼中的運行時異常。不僅闡述了處理異常的關鍵字,還瞭解了應用級和系統級異常區別和異常的基類。

7.1 .NET異常處理

.NET結構化異常處理是一項適合處理運行時異常的技術。它提供了一種標準的技術來發送和捕獲執行階段錯誤,這就是結構化異常處理(SEH)。其優點就是給開發人員有了統一的對.NET領域內各語言相同的途徑來處理異常。另外,它提供了易懂的問題描述和協助資訊。

.NET異常處理有四個要素:

(1)一個表示異常詳細資料的類類型:比如基類System.Exception類或者自訂的類。

(2)一個向調用者引發異常類執行個體的成員:也就是一個產生throw語句的地方,那麼如何知道異常類中哪些成員有哪些可能的引發異常呢?可以通過SDK文檔來查詢一個方法,那裡會列出這個方法可能引發的異常,另外,在VS.NET平台中,通過懸停滑鼠於某個方法,也可以提示這個成員可能引發的異常,如所示,它就表示,在執行ReadLind方法時,有可能拋出三種異常。

 

(3)調用者的一段調用異常成員的代碼塊:也就是有可能出錯的普通代碼,如int a=int.parse(console.writeline());

(4)調用者的一段處理(或捕獲)將要發生異常的代碼塊:try/catch塊。

所有的使用者定義和系統定義的異常最終都繼承自system.exception基類(當然,它又繼承自object)。他具有多個建構函式,可重寫的方法和屬性。它的屬性如TatgetSite、StackTrace、HelpLink、Data在擷取異常詳細資料時很有用!

7.2引發異常

我們可以在某個類中的某個方法執行時,當判斷出錯時給出一條錯誤提示資訊(如messagebox.show(“出錯啦!”)),當然,也可以使用C#的throw關鍵字將錯誤對象返回個調用者,如:

public void accelerate(int data) { //正常的一些執行代碼 if (data>100)//拋出異常 
throw new exception(“出錯啦!”); 
//當然,也可以先執行個體化一個exception,設定一些屬性,然後拋出}

注意,如果我們引發一個異常,總是由我們決定所引發的問題和何時引發異常。異常應當僅僅在一個較為致命的條件滿足後引發,決定什麼條件下引發異常是應該應對的一個設計問題。

7.3捕獲異常

因為上面已經引發了異常,那麼調用者在調用這個方法時,如果處理或者捕獲這個可能的異常呢,應該使用try/catch塊,一旦捕獲到異常對象,將能夠調用捕獲到的異常類的成員來釋放問題的詳細資料。

try() { accelerate(10);} catch(exception e) //捕獲accelerate方法有可能拋出的異常{ console.writeline(e.message); } finally {  } 

其中try是執行過程中可能引發異常的聲明的一部分。如這裡應該調用accelerate()方法寫在這裡。

如果沒有在try中觸任何異常,相應catch塊就被直接略過。如果try中的代碼觸發了異常,try的剩餘代碼將不被執行,程式立刻流入相應的catch塊,catch塊可以有多個,用於捕獲不同類型的異常,是否能進入某個catch塊,取決於捕獲的異常是否為這個catch後面聲明的異常類一致(或為它的父類)。記住,只能進入到第一個匹配的catch塊,因此,應該將特定的異常放在前面,將通用的或者範圍更廣的放在後面哦!如果捕獲到異常,但是沒有相應catch塊匹配,則執行階段錯誤將中斷程式,並彈出錯誤框哦,這將非常妨礙終端使用者使用我們的程式。如所示:

 

當然,在調試時,彈出框可以讓我們判斷錯誤的詳細資料,點擊查看詳細資料,可以查看這個異常對象的所有資訊,如所示:

 

另外,也支援通用的catch語句,它不顯示接收由指定成員引發的異常對象,也就是無需後面的(exception e),但這不是推薦方式,因為無法輸出異常的資訊,僅僅是在try捕獲異常後,執行一些異常處理的普通代碼。

再次引發異常

可以在catch中向之前的調用者再次引發一個異常,僅僅需要在這個塊中使用throw關鍵字就行了,它通過調用邏輯鏈傳遞異常,這在catch塊只能處理即將發生的部分錯誤的時候很有用:

try { } catch(CarIsDeadexception e) { //執行一些處理此錯誤的操作並傳遞異常 
throw; } 

當然,這裡沒有顯式重新拋出CarIsDeadexception對象,而是使用了不帶參數的throw關鍵字,這樣可以保留原始對象的上下文。

註:無參的throw只能在catch中,它將把當前catch塊所捕獲的異常進行拋出,應該在這個try/catch外,再用try/catch來捕獲此異常,否則將會交給CLR來捕獲,這並不是一個推薦的方式哦。

內部異常

也就是說,我們完全可以在處理其他異常的時候再觸發一個異常,例如,在catch中處理一個異常時候,需要讀取檔案,那麼有可能會出現檔案不存在的異常,因此,在這個catch中捕獲這個異常也是可以想象的。最好的習慣(推薦,但不是強制)是將這個新異常對象標識為與第一個異常類型相同的新對象中的“內部異常”。之所以需要建立一個異常的新對象來等待處理,是因為聲明一個內部異常的唯一途徑就是將其作為一個建構函式參數,下例其實就是上面的一種擴充:

try { } catch(CarIsDeadexception e) { try { } catch(exception e2) { //異常處理 
throw new CarIsDeadexception(e.message,e2);//引發記錄新異常的異常,還有第一個異常的相關資訊 } } 

注意,這個新的內部異常並沒有被外層的try/catch塊所捕獲,而是要調用者利用try/catch去捕獲,這時捕獲的異常應該是內部異常的類型(按上面所推薦的,它應該和外部例外狀況類型一致),從而可以通過InnerException來訪問捕獲的異常的詳細資料。否則將會發給CLR進行捕獲,這將導致彈出錯誤提示框。

在一個try/catch塊後面可能接著定義一個finally塊,他不是必須得,是為了保證不管是否異常,一組代碼語句始終都能被執行!

擴充話題,關於try/catch/finally:

  • 一旦try的某條語句出現異常,將不再執行try剩餘的程式碼片段,而是轉向相應catch塊,若沒有被捕獲到,則向上層調用者拋出異常。
  • 若存在try/catch/finally結構,代碼將會按照try-finally(無異常)或try-catch-finally(catch捕獲到異常)或只執行try(記得這裡將不再執行finally,這是由於catch未捕獲到指定類型異常,而向上層繼續拋出異常)的順序執行。
  • 若存在的是try/finally結構,也就是沒有catch塊,那麼代碼將按照try-finally(無異常)或只執行try(和上面的類似解釋)的順序執行。
  • 若try或catch中存在return,則一定是執行完其中某一個return後才去執行finally裡面的代碼,因此,finally裡面是無法在這種情況下改變傳回值的。
  • finally裡面不能存在return。

7.4異常類

.NET異常分為系統級異常和應用級異常。

系統級異常:.NET基底類別庫定義了許多派生自System.Exception的類,準確的說,這些由.NET平台引發的異常應該為系統異常,這些異常被認為是無法修複的致命錯誤。系統異常直接派生自System.SystemException的基類,該類派生自System.Exception。其作用就是當一個異常類派生自System.SystemException時,我們就可以判斷引發異常的實體是.NET運行庫而不是正在執行的應用程式程式碼程式庫。僅此而已。簡言之,系統級異常就是.NET平台(CLR)的各種類中事先定義好的異常,而並非使用者所編寫的。

應用級異常:自訂異常應該派生自System.ApplicationException。實際上應用程式級異常唯一目的就是標識出錯的來源。可以判斷異常是由正在執行的應用程式程式碼程式庫引發(throw)的,而不是又CLR基底類別庫或者.NET運行時引擎引發 (throw)的。

實際上上述兩種異常都不在System.Exception一組建構函式外再定義其他任何成員,僅僅是為了區別是何種類型的異常。

使用者當然可以一直引發System.Exception的執行個體來表示執行階段錯誤,但有時候構建一個強型別異常來表示當前問題的獨特細節更好!有個原則,就是何時需要構建自訂異常:僅需在出現錯誤的類與該錯誤關係緊密時才需要建立。例如,一個自訂檔案類引發許多檔案相關錯誤。

對於使用者想構建的自訂異常,建議繼承自System.ApplicationException,這是一個最佳實務,當然,繼承自System.Exception也不會出錯!而且作為一個規則,建議將自訂異常類聲明為公用類型(public),因為預設是internal類型,但是由於異常類通常都跨程式集邊界進行傳遞,所以還是公用類型好。

之後,應該去重寫父類的屬性和方法即可。

一個嚴格規範的自訂異常類,需要確保類遵循.NET異常處理的最佳實務,需要:

  • 繼承自Exception/ApplicationException類;
  • 有[System.Seralizable]特性標記;
  • 定義一個預設建構函式;
  • 定義一個設定繼承的message屬性的建構函式;
  • 定義一個處理“內部異常”的建構函式
  • 定義一個處理類型序列化的建構函式。

這樣一個規範,還好VS.NET平台提供了程式碼片段模板,它能自動產生遵循上述最佳實務的異常類,在需要建立的地方右鍵-插入程式碼片段-visual c#-exception,就可以啦!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.