国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

> 第七章 異常處理(rainbow 翻譯) (來自重粒子空間)

2024-07-21 02:28:07
字體:
來源:轉載
供稿:網(wǎng)友

<<展現(xiàn)c#>> 第七章 異常處理(rainbow 翻譯)

出處:http://www.informit.com/matter/ser0000002

正文:

第七章   異常處理

    通用語言運行時(clr)具有的一個很大的優(yōu)勢為,異常處理是跨語言被標準化的。一個在c#中所引發(fā)的異常可以在visual basic客戶中得到處理。不再有 hresults  或者 isupporterrorinfo 接口。
    盡管跨語言異常處理的覆蓋面很廣,但這一章完全集中討論c#異常處理。你稍為改變編譯器的溢出處理行為,接著有趣的事情就開始了:你處理了該異常。要增加更多的手段,隨后引發(fā)你所創(chuàng)建的異常。

7.1  校驗(checked)和非校驗(unchecked)語句
    當你執(zhí)行運算時,有可能會發(fā)生計算結果超出結果變量數(shù)據(jù)類型的有效范圍。這種情況被稱為溢出,依據(jù)不同的編程語言,你將被以某種方式通知——或者根本就沒有被通知。(c++程序員聽起來熟悉嗎?)
     那么,c#如何處理溢出的呢? 要找出其默認行為,請看我在這本書前面提到的階乘的例子。(為了方便其見,前面的例子再次在清單 7.1 中給出)

清單 7.1     計算一個數(shù)的階乘

1: using system;
2:
3: class factorial
4: {
5:  public static void main(string[] args)
6:  {
7:   long nfactorial = 1;
8:   long ncomputeto = int64.parse(args[0]);
9:
10:   long ncurdig = 1;
11:   for (ncurdig=1;ncurdig <= ncomputeto; ncurdig++)
12:    nfactorial *= ncurdig;
13:
14:   console.writeline("{0}! is {1}",ncomputeto, nfactorial);
15:  }
16: }

    當你象這樣使用命令行執(zhí)行程序時
    factorial 2000

    結果為0,什么也沒有發(fā)生。因此,設想c#默默地處理溢出情況而不明確地警告你是安全的。
     通過給整個應用程序(經(jīng)編譯器開關)或于語句級允許溢出校驗,你就可以改變這種行為。以下兩節(jié)分別解決一種方案。
7.1.1 給溢出校驗設置編譯器
    如果你想給整個應用程序控制溢出校驗,c#編譯器設置選擇是正是你所要找的。默認地,溢出校驗是禁用的。要明確地要求它,運行以下編譯器命令:
csc factorial.cs /checked+

    現(xiàn)在當你用2000參數(shù)執(zhí)行應用程序時,clr通知你溢出異常(見圖 7.1)。

圖 7.1 允許了溢出異常,階乘代碼產(chǎn)生了一個異常。

  按ok鍵離開對話框揭示了異常信息:
exception occurred: system.overflowexception
  at factorial.main(system.string[])

  現(xiàn)在你了解了溢出條件引發(fā)了一個 system.overflowexception異常。下一節(jié),在我們完成語法校驗之后,如何捕獲并處理所出現(xiàn)的異常?
7.1.2 語法溢出校驗
  如果你不想給整個應用程序允許溢出校驗,僅給某些代碼段允許校驗,你可能會很舒適。對于這種場合,你可能象清單7.2中顯示的那樣,使用校驗語句。

清單 7.2  階乘計算中的溢出校驗

1: using system;
2:
3: class factorial
4: {
5:  public static void main(string[] args)
6:  {
7:   long nfactorial = 1;
8:   long ncomputeto = int64.parse(args[0]);
9:
10:   long ncurdig = 1;
11:
12:   for (ncurdig=1;ncurdig <= ncomputeto; ncurdig++)
13:    checked { nfactorial *= ncurdig; }
14:
15:   console.writeline("{0}! is {1}",ncomputeto, nfactorial);
16:  }
17: }

  甚至就如你運用標志 checked-編譯了該代碼,在第13行中,溢出校驗仍然會對乘法實現(xiàn)檢查。錯誤信息保持一致。

  顯示相反行為的語句是非校驗(unchecked )。甚至如果允許了溢出校驗(給編譯器加上checked+標志),被unchecked 語句所括住的代碼也將不會引發(fā)溢出異常:

unchecked
{
nfactorial *= ncurdig;
}
  
    

7.2  異常處理語句
  既然你知道了如何產(chǎn)生一個異常(你會發(fā)現(xiàn)更多的方法,相信我),仍然存在如何處理它的問題。如果你是一個 c++ win32 程序員,肯定熟悉seh(結構異常處理)。你將從中找到安慰,c#中的命令幾乎是相同的,而且它們也以相似的方式運作。

the following three sections introduce c#'s exception-handling statements:
  以下三節(jié)介紹了c#的異常處理語句:

。用 try-catch 捕獲異常
。用try-finally 清除異常
。用try-catch-finally 處理所有的異常

7.2.1  使用 try 和 catch捕獲異常
  你肯定會對一件事非常感興趣——不要提示給用戶那令人討厭的異常消息,以便你的應用程序繼續(xù)執(zhí)行。要這樣,你必須捕獲(處理)該異常。
    這樣使用的語句是try 和 catch。try包含可能會產(chǎn)生異常的語句,而catch處理一個異常,如果有異常存在的話。清單7.3 用try 和 catch為overflowexception 實現(xiàn)異常處理。

   清單7.3  捕獲由factorial calculation引發(fā)的overflowexception 異常

1: using system;
2:
3: class factorial
4: {
5:  public static void main(string[] args)
6:  {
7:   long nfactorial = 1, ncurdig=1;
8:   long ncomputeto = int64.parse(args[0]);
9:
10:   try
11:   {
12:    checked
13:    {
14:     for (;ncurdig <= ncomputeto; ncurdig++)
15:      nfactorial *= ncurdig;
16:    }
17:   }
18:   catch (overflowexception oe)
19:   {
20:    console.writeline("computing {0} caused an overflow exception", ncomputeto);
21:    return;
22:   }
23:
24:   console.writeline("{0}! is {1}",ncomputeto, nfactorial);
25:  }
26: }

    為了說明清楚,我擴展了某些代碼段,而且我也保證異常是由checked 語句產(chǎn)生的,甚至當你忘記了編譯器設置時。
    正如你所見,異常處理并不麻煩。你所有要做的是:在try語句中包含容易產(chǎn)生異常的代碼,接著捕獲異常,該異常在這個例子中是overflowexception類型。無論一個異常什么時候被引發(fā),在catch段里的代碼會注意進行適當?shù)奶幚怼?br>    如果你不事先知道哪一種異常會被預期,而仍然想處于安全狀態(tài),簡單地忽略異常的類型。

try
{
...
}
catch
{
...
}

    但是,通過這個途徑,你不能獲得對異常對象的訪問,而該對象含有重要的出錯信息。一般化異常處理代碼象這樣:

try
{
...
}
catch(system.exception e)
{
...
}

    注意,你不能用ref或out 修飾符傳遞 e 對象給一個方法,也不能賦給它一個不同的值。

7.2.2  使用 try 和 finally 清除異常
    如果你更關心清除而不是錯誤處理, try 和 finally 會獲得你的喜歡。它不僅抑制了出錯消息,而且所有包含在 finally 塊中的代碼在異常被引發(fā)后仍然會被執(zhí)行。
    盡管程序不正常終止,但你還可以為用戶獲取一條消息,如清單 7.4 所示。

    清單 7.4  在finally 語句中處理異常

1: using system;
2:
3: class factorial
4: {
5:  public static void main(string[] args)
6:  {
7:   long nfactorial = 1, ncurdig=1;
8:   long ncomputeto = int64.parse(args[0]);
9:   bool ballfine = false;
10:
11:   try
12:   {
13:    checked
14:    {
15:     for (;ncurdig <= ncomputeto; ncurdig++)
16:      nfactorial *= ncurdig;
17:    }
18:    ballfine = true;
19:   }
20:   finally
21:   {
22:    if (!ballfine)
23:     console.writeline("computing {0} caused an overflow exception", ncomputeto);
24:    else
25:     console.writeline("{0}! is {1}",ncomputeto, nfactorial);
26:   }
27:  }
28: }

    通過檢測該代碼,你可能會猜到,即使沒有引發(fā)異常處理,finally也會被執(zhí)行。這是真的——在finally中的代碼總是會被執(zhí)行的,不管是否具有異常條件。為了舉例說明如何在兩種情況下提供一些有意義的信息給用戶, 我引進了新變量ballfine。ballfine告訴finally 語段,它是否是因為一個異常或者僅是因為計算的順利完成而被調(diào)用。
    作為一個習慣了seh程序員,你可能會想,是否有一個與__leave 語句等價的語句,該語句在c++中很管用。如果你還不了解,在c++中的__leave 語句是用來提前終止 try  語段中的執(zhí)行代碼,并立即跳轉到finally 語段 。
    壞消息, c# 中沒有__leave 語句。但是,在清單 7.5 中的代碼演示了一個你可以實現(xiàn)的方案。

    清單 7.5  從 try語句 跳轉到finally 語句

1: using system;
2:
3: class jumptest
4: {
5:  public static void main()
6:  {
7:   try
8:   {
9:    console.writeline("try");
10:    goto __leave;
11:   }
12:   finally
13:   {
14:    console.writeline("finally");
15:   }
16:
17:   __leave:
18:   console.writeline("__leave");
19:  }
20: }


    當這個應用程序運行時,輸出結果為

try
finally
__leave

     一個 goto 語句不能退出 一個finally 語段。甚至把 goto 語句放在 try 語句 段中,還是會立即返回控制到 finally 語段。因此,goto 只是離開了 try 語段并跳轉到finally 語段。直到 finally 中的代碼完成運行后,才能到達__leave 標簽。按這種方式,你可以模仿在seh中使用的的__leave 語句。
     順便地,你可能懷疑goto 語句被忽略了,因為它是try 語句中的最后一條語句,并且控制自動地轉移到了 finally 。為了證明不是這樣,試把goto 語句放到console.writeline 方法調(diào)用之前。盡管由于不可到達代碼你得到了編譯器的警告,但是你將看到goto語句實際上被執(zhí)行了,且沒有為 try 字符串產(chǎn)生的輸出。

7.2.3  使用try-catch-finally處理所有異常
    應用程序最有可能的途徑是合并前面兩種錯誤處理技術——捕獲錯誤、清除并繼續(xù)執(zhí)行應用程序。所有你要做的是在出錯處理代碼中使用 try 、catch 和 finally語句。清單 7.6 顯示了處理零除錯誤的途徑。

    清單 7.6  實現(xiàn)多個catch 語句

1: using system;
2:
3: class catchit
4: {
5:  public static void main()
6:  {
7:   try
8:   {
9:    int nthezero = 0;
10:    int nresult = 10 / nthezero;
11:   }
12:   catch(dividebyzeroexception divex)
13:   {
14:    console.writeline("divide by zero occurred!");
15:   }
16:   catch(exception ex)
17:   {
18:    console.writeline("some other exception");
19:   }
20:   finally
21:   {
22:   }
23:  }
24: }

    這個例子的技巧為,它包含了多個catch 語句。第一個捕獲了更可能出現(xiàn)的dividebyzeroexception異常,而第二個catch語句通過捕獲普通異常處理了所有剩下來的異常。
    你肯定總是首先捕獲特定的異常,接著是普通的異常。如果你不按這個順序捕獲異常,會發(fā)生什么事呢?清單7.7中的代碼有說明。

  清單7.7   順序不適當?shù)?catch 語句

1:   try
2:   {
3:    int nthezero = 0;
4:    int nresult = 10 / nthezero;
5:   }
6:   catch(exception ex)
7:   {
8:    console.writeline("exception " + ex.tostring());
9:   }
10:   catch(dividebyzeroexception divex)
11:   {
12:    console.writeline("never going to see that");
13:   }


    編譯器將捕獲到一個小錯誤,并類似這樣報告該錯誤:
wrongcatch.cs(10,9): error cs0160: a previous catch clause already
catches all exceptions of this or a super type ('system.exception')

    最后,我必須告發(fā)clr異常與seh相比時的一個缺點(或差別):沒有 exception_continue_execution標識符的等價物,它在seh異常過濾器中很有用。基本上,exception_continue_execution 允許你重新執(zhí)行負責異常的代碼片段。在重新執(zhí)行之前,你有機會更改變量等。我個人特別喜歡的技術為,使用訪問違例異常,按需要實施內(nèi)存分配。
  

7.3  引發(fā)異常
    當你必須捕獲異常時,其他人首先必須首先能夠引發(fā)異常。而且,不僅其他人能夠引發(fā),你也可以負責引發(fā)。其相當簡單:

throw new argumentexception("argument can't be 5");
    你所需要的是throw 語句和一個適當?shù)漠惓n悺N乙呀?jīng)從表7.1提供的清單中選出一個異常給這個例子。
    
    表 7.1   runtime提供的標準異常


異常類型                                                        描述

exception                                            所有異常對象的基類
systemexception                                      運行時產(chǎn)生的所有錯誤的基類
indexoutofrangeexception                             當一個數(shù)組的下標超出范圍時運行時引發(fā)
nullreferenceexception                               當一個空對象被引用時運行時引發(fā)
invalidoperationexception                            當對方法的調(diào)用對對象的當前狀態(tài)無效時,由某些方法引發(fā)
argumentexception                                    所有參數(shù)異常的基類
argumentnullexception                                在參數(shù)為空(不允許)的情況下,由方法引發(fā)
argumentoutofrangeexception                          當參數(shù)不在一個給定范圍之內(nèi)時,由方法引發(fā)
interopexception                                     目標在或發(fā)生在clr外面環(huán)境中的異常的基類
comexception                                         包含com 類的hresult信息的異常
sehexception                                         封裝win32 結構異常處理信息的異常

    然而,在catch語句的內(nèi)部,你已經(jīng)有了隨意處置的異常,就不必創(chuàng)建一個新異常。可能在表7.1 中的異常沒有一個符合你特殊的要求——為什么不創(chuàng)建一個新的異常?在即將要學到小節(jié)中,都涉及到這兩個話題。

7.3.1 重新引發(fā)異常
    當處于一個catch 語句的內(nèi)部時,你可能決定引發(fā)一個目前正在再度處理的異常,留下進一步的處理給一些外部的try-catch 語句。該方法的例子如 清單7.8所示。

清單 7.8  重新引發(fā)一個異常

1:   try
2:   {
3:    checked
4:    {
5:     for (;ncurdig <= ncomputeto; ncurdig++)
6:      nfactorial *= ncurdig;
7:    }
8:   }
9:   catch (overflowexception oe)
10:   {
11:    console.writeline("computing {0} caused an overflow exception", ncomputeto);
12:    throw;
13:   }

    注意,我不必規(guī)定所聲明的異常變量。盡管它是可選的,但你也可以這樣寫:
    throw oe;
    現(xiàn)在有時還必須留意這個異常。

7.3.2  創(chuàng)建自己的異常類
    盡管建議使用預定義的異常類,但對于實際場合,創(chuàng)建自己的異常類可能會方便。創(chuàng)建自己的異常類,允許你的異常類的使用者根據(jù)該異常類采取不同的手段。
    在清單 7.9 中出現(xiàn)的異常類 myimportantexception遵循兩個規(guī)則:第一,它用exception結束類名。第二,它實現(xiàn)了所有三個被推薦的通用結構。你也應該遵守這些規(guī)則。
    清單  7.9   實現(xiàn)自己的異常類  myimportantexception

1: using system;
2:
3: public class myimportantexception:exception
4: {
5:  public myimportantexception()
6:   :base() {}
7:
8:  public myimportantexception(string message)
9:   :base(message) {}
10:
11:  public myimportantexception(string message, exception inner)
12:   :base(message,inner) {}
13: }
14:
15: public class exceptiontestapp
16: {
17:  public static void testthrow()
18:  {
19:   throw new myimportantexception("something bad has happened.");
20:  }
21:
22:  public static void main()
23:  {
24:   try
25:   {
26:    exceptiontestapp.testthrow();
27:   }
28:   catch (exception e)
29:   {
30:    console.writeline(e);
31:   }
32:  }
33: }

    正如你所看到的,myimportantexception 異常類不能實現(xiàn)任何特殊的功能,但它完全基于system.exception類。程序的剩余部分測試新的異常類,給system.exception 類使用一個catch 語句。
    如果沒有特殊的實現(xiàn)而只是給myimportantexception定義了三個構造函數(shù),創(chuàng)建它又有什么意義呢?它是一個重要的類型——你可以在catch語句中使用它,代替更為普通的異常類。可能引發(fā)你的新異常的客戶代碼可以按規(guī)定的catch代碼發(fā)揮作用。
    當使用自己的名字空間編寫一個類庫時,也要把異常放到該名字空間。盡管它并沒有出現(xiàn)在這個例子中,你還是應該使用適當?shù)膶傩裕瑸閿U展了的錯誤信息擴充你的異常類。

7.4  異常處理的“要”和“不要”
  作為最后的忠告之語,這里是對異常引發(fā)和處理所要做和不要做的清單:
。當引發(fā)異常時,要提供有意義的文本。
。要引發(fā)異常僅當條件是真正異常;也就是當一個正常的返回值不滿足時。
。如果你的方法或屬性被傳遞一個壞參數(shù),要引發(fā)一個argumentexception異常。
。當調(diào)用操作不適合對象的當前狀態(tài)時,要引發(fā)一個 invalidoperationexception異常。
。要引發(fā)最適合的異常。
。要使用鏈接異常,它們允許你跟蹤異常樹。
。不要為正常或預期的錯誤使用異常。
。不要為流程的正常控制使用異常。
。不要在方法中引發(fā) nullreferenceexception或indexoutofrangeexception異常。

   7.5  小結
    這一章由介紹溢出校驗開始。你可以使用編譯器開關(默認是關),使整個應用程序允許或禁止溢出校驗。如果需要微調(diào)控制,你可以使用校驗和非校驗語句,它允許你使用或不使用溢出校驗來執(zhí)行一段代碼,盡管沒有給應用程序設置開關。
    當發(fā)生溢出時,一個異常就被引發(fā)了。如何處理異常取決于你。我提出了各種途徑,包括你最有可能貫穿整個應用程序使用的:try、catch 和finally 語句。在伴隨的多個例子中,你學到了它與win32結構異常處理(seh)的差別。
   異常處理是給類的用戶; 然而,如果你負責創(chuàng)建新的類,就可以引發(fā)異常。有多種選擇:引發(fā)早已捕獲的異常,引發(fā)存在的框架異常,或者按規(guī)定的實際目標創(chuàng)建新的異常類。
   最后,你需要閱讀引發(fā)和處理異常的各種“要”和


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 息烽县| 建宁县| 博白县| 秦安县| 古丈县| 云阳县| 凤翔县| 厦门市| 英超| 铜陵市| 颍上县| 洛阳市| 广德县| 公主岭市| 资兴市| 荆门市| 濮阳市| 体育| 宁陕县| 柘城县| 蒲城县| 左云县| 高安市| 溧水县| 鹤壁市| 宜黄县| 上高县| 秦皇岛市| 南丰县| 承德县| 达拉特旗| 虎林市| 马关县| 资源县| 宁阳县| 高要市| 榆中县| 犍为县| 龙里县| 犍为县| 思南县|