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

首頁(yè) > 編程 > .NET > 正文

《.net編程先鋒C#》第七章 異常處理(轉(zhuǎn))

2024-07-10 13:00:27
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
第七章 異常處理
通用語(yǔ)言運(yùn)行時(shí)(clr)具有的一個(gè)很大的優(yōu)勢(shì)為,異常處理是跨語(yǔ)言被標(biāo)準(zhǔn)化的。一個(gè)在c#中所引發(fā)的異常可以在visual basic客戶中得到處理。不再有 hresults 或者 isupporterrorinfo 接口。
盡管跨語(yǔ)言異常處理的覆蓋面很廣,但這一章完全集中討論c#異常處理。你稍為改變編譯器的溢出處理行為,接著有趣的事情就開(kāi)始了:你處理了該異常。要增加更多的手段,隨后引發(fā)你所創(chuàng)建的異常。

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

清單 7.1 計(jì)算一個(gè)數(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: }

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

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

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

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

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

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

清單 7.2  階乘計(jì)算中的溢出校驗(yàn)

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: }

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

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

unchecked
{
nfactorial *= ncurdig;
}



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

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

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

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

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

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

但是,通過(guò)這個(gè)途徑,你不能獲得對(duì)異常對(duì)象的訪問(wèn),而該對(duì)象含有重要的出錯(cuò)信息。一般化異常處理代碼象這樣:

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

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

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

清單 7.4 在finally 語(yǔ)句中處理異常

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: }

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

清單 7.5 從 try語(yǔ)句 跳轉(zhuǎn)到finally 語(yǔ)句

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: }


當(dāng)這個(gè)應(yīng)用程序運(yùn)行時(shí),輸出結(jié)果為

try
finally
__leave

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

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

清單 7.6 實(shí)現(xiàn)多個(gè)catch 語(yǔ)句

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: }

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

清單7.7 順序不適當(dāng)?shù)?catch 語(yǔ)句

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: }


編譯器將捕獲到一個(gè)小錯(cuò)誤,并類似這樣報(bào)告該錯(cuò)誤:
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相比時(shí)的一個(gè)缺點(diǎn)(或差別):沒(méi)有 exception_continue_execution標(biāo)識(shí)符的等價(jià)物,它在seh異常過(guò)濾器中很有用。基本上,exception_continue_execution 允許你重新執(zhí)行負(fù)責(zé)異常的代碼片段。在重新執(zhí)行之前,你有機(jī)會(huì)更改變量等。我個(gè)人特別喜歡的技術(shù)為,使用訪問(wèn)違例異常,按需要實(shí)施內(nèi)存分配。


7.3 引發(fā)異常
當(dāng)你必須捕獲異常時(shí),其他人首先必須首先能夠引發(fā)異常。而且,不僅其他人能夠引發(fā),你也可以負(fù)責(zé)引發(fā)。其相當(dāng)簡(jiǎn)單:

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

表 7.1 runtime提供的標(biāo)準(zhǔn)異常


異常類型 描述

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

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

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

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

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)在有時(shí)還必須留意這個(gè)異常。

7.3.2 創(chuàng)建自己的異常類
盡管建議使用預(yù)定義的異常類,但對(duì)于實(shí)際場(chǎng)合,創(chuàng)建自己的異常類可能會(huì)方便。創(chuàng)建自己的異常類,允許你的異常類的使用者根據(jù)該異常類采取不同的手段。
在清單 7.9 中出現(xiàn)的異常類 myimportantexception遵循兩個(gè)規(guī)則:第一,它用exception結(jié)束類名。第二,它實(shí)現(xiàn)了所有三個(gè)被推薦的通用結(jié)構(gòu)。你也應(yīng)該遵守這些規(guī)則。
清單 7.9 實(shí)現(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 異常類不能實(shí)現(xiàn)任何特殊的功能,但它完全基于system.exception類。程序的剩余部分測(cè)試新的異常類,給system.exception 類使用一個(gè)catch 語(yǔ)句。
如果沒(méi)有特殊的實(shí)現(xiàn)而只是給myimportantexception定義了三個(gè)構(gòu)造函數(shù),創(chuàng)建它又有什么意義呢?它是一個(gè)重要的類型——你可以在catch語(yǔ)句中使用它,代替更為普通的異常類。可能引發(fā)你的新異常的客戶代碼可以按規(guī)定的catch代碼發(fā)揮作用。
當(dāng)使用自己的名字空間編寫一個(gè)類庫(kù)時(shí),也要把異常放到該名字空間。盡管它并沒(méi)有出現(xiàn)在這個(gè)例子中,你還是應(yīng)該使用適當(dāng)?shù)膶傩裕瑸閿U(kuò)展了的錯(cuò)誤信息擴(kuò)充你的異常類。

7.4 異常處理的“要”和“不要”
作為最后的忠告之語(yǔ),這里是對(duì)異常引發(fā)和處理所要做和不要做的清單:
。當(dāng)引發(fā)異常時(shí),要提供有意義的文本。
。要引發(fā)異常僅當(dāng)條件是真正異常;也就是當(dāng)一個(gè)正常的返回值不滿足時(shí)。
。如果你的方法或?qū)傩员粋鬟f一個(gè)壞參數(shù),要引發(fā)一個(gè)argumentexception異常。
。當(dāng)調(diào)用操作不適合對(duì)象的當(dāng)前狀態(tài)時(shí),要引發(fā)一個(gè) invalidoperationexception異常。
。要引發(fā)最適合的異常。
。要使用鏈接異常,它們?cè)试S你跟蹤異常樹(shù)。
。不要為正常或預(yù)期的錯(cuò)誤使用異常。
。不要為流程的正常控制使用異常。
。不要在方法中引發(fā) nullreferenceexception或indexoutofrangeexception異常。

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

中國(guó)最大的web開(kāi)發(fā)資源網(wǎng)站及技術(shù)社區(qū),
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 米易县| 滕州市| 东乌| 兰坪| 鄂托克旗| 石泉县| 鹤山市| 杭锦旗| 泸西县| 米泉市| 息烽县| 上蔡县| 富平县| 手游| 三原县| 乐都县| 博兴县| 遵化市| 历史| 大港区| 武夷山市| 故城县| 盘山县| 密山市| 石狮市| 辽宁省| 理塘县| 象山县| 大同市| 铜梁县| 龙井市| 普兰店市| 定远县| 扎兰屯市| 公安县| 焉耆| 兴国县| 天全县| 天门市| 万宁市| 江门市|