展開
湖北國(guó)聯(lián)計(jì)算機(jī)科技有限公司
  • 首頁(yè)HOME
  • 公司簡(jiǎn)介INTRODUCTION
  • 安全防御DEFENSE
  • 軟件開發(fā)SOFTWARE
  • 物聯(lián)網(wǎng)IOT
  • 運(yùn)行維護(hù)SRE
  • 成功案例CASE
  • 聯(lián)系我們CONTACT
  • Software Technology Sharing |技術(shù)分享

    國(guó)菱程序員之——C#異常
    來源:荊州網(wǎng)站建設(shè) 時(shí)間:2017-08-02

       湖北國(guó)菱計(jì)算機(jī)科技有限公司荊州軟件開發(fā)部工程師小宇會(huì)定期跟大家交流C#使用過程中總結(jié)的經(jīng)驗(yàn)及心得,歡迎伙伴們一起交流,郵箱:business@gl-ns.com。今天跟大家談?wù)凜# 異常處理機(jī)制。  

       

    異常簡(jiǎn)介

    C sharp中的異常用于處理系統(tǒng)級(jí)和應(yīng)用程序級(jí)的錯(cuò)誤狀態(tài),它是一種結(jié)構(gòu)化、統(tǒng)一的類型安全的處理機(jī)制。異常處理相對(duì)于返回錯(cuò)誤代碼的一個(gè)最大優(yōu)點(diǎn)在于,異??梢员蛔詣?dòng)傳遞,這樣,在編程時(shí)異常更加難以被忽視。

    C#的異常機(jī)制非常類似于C++的異常處理機(jī)制,但是還是有一些重要的區(qū)別:

    1,在C#中,所有的異常必須由從System.Exception派生的類來表示。在 C++ 中,可以使用任何類型的任何值表示異常。

    2,在C#中,利用finally 塊可編寫在正常執(zhí)行和異常情況下都將執(zhí)行的終止代碼。在C++中,很難在不重復(fù)代碼的情況下編寫這樣的代碼。

    3,C# 中,系統(tǒng)級(jí)的異常如溢出、被零除和 null 等都對(duì)應(yīng)地定義了與其匹配的異常類,并且與應(yīng)用程序級(jí)的錯(cuò)誤狀態(tài)處于同等地位。


    引發(fā)異常的原因

    1.throw 語(yǔ)句用于立即無(wú)條件地引發(fā)異常??刂朴肋h(yuǎn)不會(huì)到達(dá)緊跟在 throw 后面的語(yǔ)句。

    2.在執(zhí)行C#語(yǔ)句和表達(dá)式的過程中,有時(shí)會(huì)出現(xiàn)一些例外情況,使某些操作無(wú)法正常完成,此時(shí)就會(huì)引發(fā)一個(gè)異常。例如,在整數(shù)除法運(yùn)算中,若分母為零引發(fā) System.DivideByZeroException。

    System.Exception 類

    System.Exception類是所有異常的基類型。若干個(gè)異常類直接從Exception繼承。

    ApplicationExceptionSystemException繼承該類,幾乎是所有運(yùn)行時(shí)異常的基礎(chǔ)。

    此類具有一些所有異常共享的值得注意的屬性:

    · Message 是string類型的一個(gè)只讀屬性,它包含關(guān)于所發(fā)生異常的原因的描述(易于人工閱讀)。

    · InnerException 是 Exception 類型的一個(gè)只讀屬性。

    如果它的值不是 null,則它所引用的是指導(dǎo)致了當(dāng)前異常的那個(gè)異常,即表示當(dāng)前異常是在處理那個(gè)InnerException的catch 塊中被引發(fā)的。

    如果它的值為 null,則表示該異常不是由另一個(gè)異常引發(fā)的。

    以這種方式鏈接在一起的異常對(duì)象的數(shù)目可以是任意的。此屬性可用來在異常處理過程中創(chuàng)建和保留一系列異常。可使用此屬性創(chuàng)建一個(gè)新異常來包含以前捕捉的異常。原始異常可

    由 InnerException 屬性中的第二個(gè)異常捕獲,這使處理第二個(gè)異常的代碼可以檢查附加信息。

    例如,假設(shè)有一個(gè)讀取文件并格式化相應(yīng)數(shù)據(jù)的方法。 代碼嘗試從文件讀取,但引發(fā)FileException。該方法捕捉 FileException 并引發(fā) BadFormatException。在此情況下,F(xiàn)ileException 可保存在 BadFormatException 的 InnerException 屬性中。

    為提高調(diào)用方確定異常引發(fā)原因的能力,有時(shí)可能需要方法捕捉幫助器例程引發(fā)的異常,然后引發(fā)一個(gè)進(jìn)一步指示已發(fā)生的錯(cuò)誤的異常。 可以創(chuàng)建一個(gè)更有意義的新異常,其中內(nèi)部異常引用可以設(shè)置為原始異常。 然后可以針對(duì)調(diào)用方引發(fā)這種更有意義的異常。 請(qǐng)注意,使用此功能,可以創(chuàng)建以最先引發(fā)的異常作為結(jié)束點(diǎn)的一系列相鏈接的異常。

    ·StackTrace 屬性

    此屬性包含可用來確定錯(cuò)誤發(fā)生位置的堆棧跟蹤。如果有可用的調(diào)試信息,則堆棧跟蹤包含源文件名和程序行號(hào)。

    ·Data 屬性:此屬性是可以保存任意數(shù)據(jù)(以鍵值對(duì)的形式)的IDictionary。


    異常的處理方式

    先執(zhí)行try里面的語(yǔ)句,如果try里面的語(yǔ)句拋出了錯(cuò)誤,就會(huì)被catch捕獲,所以就會(huì)中斷try里面語(yǔ)句的執(zhí)行轉(zhuǎn)而執(zhí)行catch里面的語(yǔ)句,如果try里面的語(yǔ)句都執(zhí)行完了也沒有拋出錯(cuò)誤,那么catch里的語(yǔ)句就沒有機(jī)會(huì)執(zhí)行了。最后不論try順利運(yùn)行完畢,還是try拋出了錯(cuò)誤被catch語(yǔ)句捕獲并執(zhí)行了catch的語(yǔ)句都要接著執(zhí)行finally里面的語(yǔ)句。

    發(fā)生異常時(shí),系統(tǒng)將搜索可以處理該異常的最近的 catch 子句(根據(jù)該異常的運(yùn)行時(shí)類型來確定)。首先,搜索當(dāng)前的方法以查找一個(gè)詞法上包含著它的 try 語(yǔ)句,并按順序考察與該 try 語(yǔ)句相關(guān)聯(lián)的各個(gè) catch 子句。如果上述操作失敗,則在調(diào)用了當(dāng)前方法的方法中,搜索在詞法上包含著當(dāng)前方法調(diào)用代碼位置的 try 語(yǔ)句。此搜索將一直進(jìn)行下去,直到找到可以處理當(dāng)前異常的 catch 子句(該子句指定一個(gè)異常類,它與當(dāng)前引發(fā)該異常的運(yùn)行時(shí)類型屬于同一個(gè)類或是該運(yùn)行時(shí)類型所屬類的一個(gè)基類)。注意,沒有指定異常類的 catch 子句可以處理任何異常。找到匹配的 catch 子句后,系統(tǒng)將把控制轉(zhuǎn)移到該 catch 子句的第一條語(yǔ)句。在 catch 子句的執(zhí)行開始前,系統(tǒng)將首先按順序執(zhí)行嵌套在捕捉到該異常的 try 語(yǔ)句里面的所有 try 語(yǔ)句所對(duì)應(yīng)的全部 finally 子句。

    如果沒有找到匹配的 catch 子句,則發(fā)生下列兩種情況之一:

    · 如果對(duì)匹配的 catch 子句的搜索到達(dá)一個(gè)靜態(tài)構(gòu)造函數(shù)或靜態(tài)字段初始值設(shè)定項(xiàng),則在導(dǎo)致調(diào)用該靜態(tài)構(gòu)造函數(shù)的代碼位置引發(fā) System.TypeInitializationException。該 System.TypeInitializationException 的內(nèi)部異常將包含最初引發(fā)的異常。

    · 如果對(duì)匹配的 catch 子句的搜索到達(dá)最初啟動(dòng)當(dāng)前線程的代碼處,則該線程的執(zhí)行就會(huì)終止。此類終止會(huì)產(chǎn)生什么影響,應(yīng)由實(shí)現(xiàn)來定義。

    特別值得注意的是在析構(gòu)函數(shù)執(zhí)行過程中發(fā)生的異常。如果在析構(gòu)函數(shù)執(zhí)行過程中發(fā)生異常且該異常未被捕獲,則將終止該析構(gòu)函數(shù)的執(zhí)行,并調(diào)用它的基類的析構(gòu)函數(shù)(如果有)。如果沒有基類(如 object 類型中的情況),或者如果沒有基類析構(gòu)函數(shù),則該異常將被忽略。


    異常類的層次結(jié)構(gòu)
    System.ArrayTypeMismatchException:當(dāng)存儲(chǔ)一個(gè)數(shù)組時(shí),如果由于被存儲(chǔ)的元素的實(shí)際類型與數(shù)組的實(shí)際類型不兼容而導(dǎo)致存儲(chǔ)失敗,就會(huì)引發(fā)此異常。

    System.DivideByZeroException:在試圖用零除整數(shù)值時(shí)引發(fā)。

    System.IndexOutOfRangeException:在試圖使用小于零或超出數(shù)組界限的下標(biāo)索引數(shù)組時(shí)引發(fā)。

    System.InvalidCastException:當(dāng)從基類型或接口到派生類型的顯式轉(zhuǎn)換在運(yùn)行時(shí)失敗時(shí)引發(fā)。

    System.NullReferenceException:在需要使用引用對(duì)象的場(chǎng)合,如果使用 null 引用時(shí)引發(fā)。

    System.OutOfMemoryException:在分配內(nèi)存(通過 new)的嘗試失敗時(shí)引發(fā)。

    System.OverflowException:在 checked 上下文中的算術(shù)運(yùn)算溢出時(shí)引發(fā)。

    System.StackOverflowException:當(dāng)執(zhí)行堆棧由于保存了太多掛起的方法調(diào)用而耗盡時(shí),就會(huì)引發(fā)此異常;這通常表明存在非常深或無(wú)限的遞歸。

    System.TypeInitializationException:在靜態(tài)構(gòu)造函數(shù)引發(fā)異常并且沒有可以捕捉到它的 catch 子句時(shí)引發(fā)。


    產(chǎn)生TypeInitializationException的情況就包含以下幾種:

    1. 訪問類的某一靜態(tài)成員,而其他靜態(tài)成員的初始化(或靜態(tài)構(gòu)造函數(shù)中)產(chǎn)生異常。

    例如訪問ClassHelper.StaticString,由于靜態(tài)成員Field的初始化產(chǎn)生異常,因此調(diào)用ClassHelper.StaticString會(huì)拋出TypeInitializationException。

    1. 訪問類的某一靜態(tài)成員,該靜態(tài)成員的初始化(或靜態(tài)構(gòu)造函數(shù)中)產(chǎn)生異常。

    例如訪問ClassHelper.Field。

    1. 對(duì)該類進(jìn)行初始化,而類中的某個(gè)靜態(tài)成員初始化(或靜態(tài)構(gòu)造函數(shù)中)產(chǎn)生異常。

    例如ClassHelper helper = new ClassHelper()。  



    異常處理準(zhǔn)則

    1. 通常只在最上層(一般是UI層)捕捉異常。如果要在其它層捕捉異常,除非是下列情況之一:
      (1)能夠處理該異常
      (2)能夠忽略該異常
      (3)需要轉(zhuǎn)換該異常為其它特定異常后拋出新異常

    2. UI層捕獲異常后,可以
      (1)將無(wú)關(guān)緊要的異常忽略。
      (2)將異常轉(zhuǎn)換為錯(cuò)誤信息展現(xiàn)給用戶。
      (3)如果是重大異常,可以考慮終止應(yīng)用程序。

    3. 如果產(chǎn)生Exception,給用戶提供一個(gè)友好的信息,但記錄與有關(guān)錯(cuò)誤的所有可能的細(xì)節(jié),包括它的發(fā)生時(shí)間,方法和類的名字等實(shí)際的錯(cuò)誤信息。有助于診斷問題。

    4.  可以在非最上層拋出自定義異常。如果是自定義異常,請(qǐng)保證其是可序列化的,并且保證其實(shí)現(xiàn)了Exception的三個(gè)構(gòu)造函數(shù)。自定義異常不要繼承Exception基類。相反,繼承ApplicationException

    5.異常的拋出與截獲需要很多的CPU時(shí)間,不要在所有的方法中寫的try - catch。只在有可能有某個(gè)特定的異常發(fā)生的方法中使用它。

    6.始終捕獲特定的異常,而不是一般的異常和系統(tǒng)異常。

    7.當(dāng)發(fā)生異常時(shí),為了確保清理占據(jù)的資源,使用try / finally塊。在finally子句中關(guān)閉的資源。使用try / finally塊,即使發(fā)生異常,也能確保資源disposed。

    8.在一個(gè)catch塊中的代碼都應(yīng)該至少部分地處理了所捕捉的異常。否則,就不要使用catch塊。

    9.從構(gòu)造函數(shù)中拋出異常。因?yàn)闃?gòu)造函數(shù)沒有返回值,所以沒有簡(jiǎn)單的方法來想構(gòu)造函數(shù)的調(diào)用者發(fā)出構(gòu)造失敗的信號(hào),這時(shí)便可以通過拋出異常來做到。比如構(gòu)造參數(shù)與指定條件不符時(shí),就拋出一個(gè)異常。

    10.在以上前提的保證下,可以在非最上層使用AOP截獲(intercept)異常而進(jìn)行日志記錄,這樣通過日志記錄,我們可以了解系統(tǒng)的運(yùn)行狀態(tài)。也可以有一個(gè)應(yīng)用程序級(jí)(線程級(jí))的錯(cuò)誤處理程序,您可以用它處理所有一般異常。在一個(gè)'意外一般錯(cuò)誤'中,這個(gè)錯(cuò)誤處理程序應(yīng)該捕獲該異常并記錄他,除此之外,在應(yīng)用程序關(guān)閉之前應(yīng)該做出友好的信息提示或者允許用戶選擇忽略異常繼續(xù)。

    記不起在哪里看到過這樣一句話:在軟件實(shí)現(xiàn)中,異常和日志都是重要的質(zhì)量保證手段,異常和日志總是同時(shí)出現(xiàn)的??梢哉f,異常是日志記錄的重要/主要組成部分。

    調(diào)試

    使用斷點(diǎn)進(jìn)入調(diào)試模式。


    荊州地區(qū)政府網(wǎng)站建設(shè) 解決方案 專業(yè)團(tuán)隊(duì) 騰訊第三方平臺(tái) 地址:湖北省荊州市沙市區(qū)荊沙大道楚天都市佳園一期C區(qū)29棟112       地址:湖北省松滋市新江口街道才知文化廣場(chǎng)1幢1146-1151室     郵編:434200 聯(lián)系電話:0716-6666211     網(wǎng)站編輯部郵箱:business@gl-ns.com 鄂公網(wǎng)安備 42100202000212號(hào) 備案號(hào):鄂ICP備2021015094號(hào)-1     企業(yè)名稱:湖北國(guó)菱計(jì)算機(jī)科技有限公司