解析C#中斷言與異常的應(yīng)用方式及異常處理的流程控制
斷言與異常(Assertion Vs Exception)
在日常編程實(shí)踐中,斷言與異常的界限不是很明顯,這也使得它們常常沒(méi)有被正確的使用。我也在不斷的與這個(gè)模糊的怪獸搏斗,僅寫(xiě)此文和大家分享一下我的個(gè)人看法。我想我們還可以從很多角度來(lái)區(qū)別斷言和異常的使用場(chǎng)景,歡迎大家的意見(jiàn)和建議。
異常的使用場(chǎng)景:用于捕獲外部的可能錯(cuò)誤
斷言的使用場(chǎng)景:用于捕獲內(nèi)部的不可能錯(cuò)誤
我們可以先仔細(xì)分析一下我們?cè)?net中已經(jīng)存在的異常。
- System.IO.FileLoadException
- SqlException
- IOException
- ServerException
首先,我們先不將它們看成異常,因?yàn)槲覀儸F(xiàn)在還沒(méi)有在異常和斷言之間劃清界限,我們先將它們看成錯(cuò)誤。
當(dāng)我們?cè)诰幋a的第一現(xiàn)場(chǎng)考慮到可能會(huì)出現(xiàn)文件加載的錯(cuò)誤或者服務(wù)器錯(cuò)誤后,我們的第一直覺(jué)是這不是我們代碼的問(wèn)題,這是我們代碼之外的問(wèn)題。
例如下面這段代碼
public void WriteSnapShot(string fileName, IEnumerable<DbItem> items) { string format = "{0}\t{1}\t{2}\t{3}\t{4}\t{5}"; using (FileStream fs = new FileStream(fileName, FileMode.Create)) { using (StreamWriter sw = new StreamWriter(fs, Encoding.Unicode)) { ... foreach (var item in items) { sw.WriteLine(string.Format(format, new object[]{ item.dealMan, item.version, item.priority, item.bugStatus, item.bugNum, item.description})); } sw.Flush(); } } }
上面的代碼在寫(xiě)入文件,很顯然會(huì)導(dǎo)致IOException。稍微有經(jīng)驗(yàn)的程序員都會(huì)考慮到IO上可能出問(wèn)題,那我們應(yīng)該如何處理這個(gè)問(wèn)題呢?在這個(gè)上下文中,我們別無(wú)它法,只能讓這個(gè)錯(cuò)誤繼續(xù)往上拋,通知上面一層的調(diào)用者,有一個(gè)錯(cuò)誤發(fā)生了,至于上一層調(diào)用者會(huì)如何處理,不是這個(gè)函數(shù)要考慮的問(wèn)題。但在這個(gè)函數(shù)中,要記得一點(diǎn),將當(dāng)前函數(shù)中所占用的資源釋放了。因此,當(dāng)我們不能控制的外部錯(cuò)誤出現(xiàn)時(shí),我們可以將其作為異常往上拋,這時(shí),我們?cè)撌褂卯惓!?/p>
現(xiàn)在再來(lái)看看斷言,我們還是以下面的一段代碼為例子。
public Entities.SimpleBugInfo GetSimpleBugInfo(string bugNum) { var selector = DependencyFactory.Resolve<ISelector>(); var list = selector.Return<Entities.SimpleBugInfo>( reader => new Entities.SimpleBugInfo { bugNum = reader["bugNum"].ToString(), dealMan = reader["dealMan"].ToString(), description = reader["description"].ToString(), size = Convert.ToInt32(reader["size"]), fired = Convert.ToInt32(reader["fired"]), }, "select * from bugInfo", new WhereClause(bugNum, "bugNum")); Trace.Assert(list != null); if (list.Count == 0) return null; else return list[0]; }
當(dāng)我貼出這段代碼時(shí),心情有些坎坷,因?yàn)槲冶救嗽谶@里也糾結(jié)了很久,這也是我一直沒(méi)有將斷言和異常劃清界線的原因之一。
首先我們來(lái)回顧一下之前定義的斷言使用場(chǎng)景:內(nèi)部不可能發(fā)生的錯(cuò)誤。
selector.Return這段代碼是不是內(nèi)部代碼?如果我們能夠修改Return中的代碼,說(shuō)明它是內(nèi)部代碼;反之,說(shuō)明它是外部代碼。對(duì)于內(nèi)部代碼,我們可以用斷言來(lái)保護(hù)其邏輯的不變性,當(dāng)斷言被觸發(fā)時(shí),我們就可以確信是內(nèi)部代碼的錯(cuò)誤,我們應(yīng)該立即修復(fù)。
再糾結(jié)一下,假設(shè)Return是外部代碼,我們沒(méi)有辦法去修改它。那么上面的代碼可以有兩種寫(xiě)法(如果你有更多的想法,請(qǐng)賜教)。
第一種,直接拋出異常。
If(list == null) { throw new NullReferenceException(); }
第二種,調(diào)整代碼。
if(list == null || list.Count == 0) { return null; } else { return list[0]; }
當(dāng)然,還有一種就是什么也不做,讓代碼執(zhí)行下去直至系統(tǒng)為你拋出空引用錯(cuò)誤。但這種做法違背了防卸性編程的原則,我們總是應(yīng)行盡早或離錯(cuò)誤的發(fā)生地最近的地方處理錯(cuò)誤,避免錯(cuò)誤數(shù)據(jù)流向系統(tǒng)的其它地方,產(chǎn)生更加嚴(yán)重的錯(cuò)誤。
總結(jié)
對(duì)異?;驍嘌缘氖褂萌Q于你要防卸的是一個(gè)內(nèi)部錯(cuò)誤還是外部錯(cuò)誤以及你認(rèn)為它是一個(gè)內(nèi)部錯(cuò)誤或外部錯(cuò)誤。如果你決定防卸一個(gè)內(nèi)部錯(cuò)誤,那請(qǐng)果斷使用斷言,反之,請(qǐng)使用異常。
異常處理
異常處理對(duì)于流程的控制,就像拋出與捕獲一樣分為兩個(gè)方面:
如果錯(cuò)誤(或某種情況)發(fā)生,是否允許程序的控制流繼續(xù)執(zhí)行下去(異常的拋出)
如果當(dāng)前有異常發(fā)生,當(dāng)前的代碼是否有機(jī)會(huì)讓程序的控制流進(jìn)入到一個(gè)合理的狀態(tài)(異常的捕獲)
我認(rèn)為可以用以上兩條,作為判斷異常處理的準(zhǔn)繩。其實(shí)大家現(xiàn)在應(yīng)該可以發(fā)現(xiàn),這個(gè)所謂的準(zhǔn)繩的著重點(diǎn)在于異常對(duì)于流程的影響,而不再是在什么情況下才使用異常。
對(duì)于流程控制,最直接的莫過(guò)于下面這段代碼
try { foreach (var lockGroup in lockGroups) { ... foreach (var newlock in lockGroup.ToArray()) { ... if (diningBlocks.Exists(n => testLockRange.IsOverlapped(n.StartTime, n.EndTime))) { status = LockStatus.InResourceBlock; throw new LockException(); } var diningAvail = availabilities.Find(n => n.Time == newlock.StartTime.TimeOfDay); if (diningAvail == null) { status = LockStatus.Failed; throw new LockException(); } ... if (newLockQuantity > diningAvail.MaxAvail && !canOverrideLock.AllowOverBook) { status = LockStatus.Override; throw new LockException(); } else if (newLockQuantity + reservedQuantity + currentLockedAvail > diningAvail.MaxAvail && !canOverrideLock.AllowOverBook) { status = LockStatus.Override; throw new LockException(); } ... } } } catch (LockException) { return new DiningLock[] { }; }
在上面的代碼中,有兩層for循環(huán),當(dāng)最內(nèi)層出現(xiàn)某種情況時(shí),要求停止整個(gè)for循環(huán)的執(zhí)行,顯然用兩個(gè)break是不行的,還得加入一個(gè)輔助變量。
但是,如果用異常,這個(gè)處理就簡(jiǎn)單多了??梢灾苯釉谧顑?nèi)層的拋出異常,在最外層(或是流程控制需要的地方)捕獲異常。
在上面的代碼中,異常處理起到了流程控制的作用,而不僅僅傳遞錯(cuò)誤信息,對(duì)代碼的簡(jiǎn)化做出了貢獻(xiàn)。
上一篇:日常收集C#接口知識(shí)(知識(shí)全面)
欄 目:C#教程
下一篇:C# 調(diào)用 JavaWebservice服務(wù)遇到的問(wèn)題匯總
本文標(biāo)題:解析C#中斷言與異常的應(yīng)用方式及異常處理的流程控制
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/6762.html
您可能感興趣的文章
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并打開(kāi)的方法
- 01-10C#實(shí)現(xiàn)Winform中打開(kāi)網(wǎng)頁(yè)頁(yè)面的方法
- 01-10C#實(shí)現(xiàn)由四周向中心縮小的窗體退出特效
- 01-10Extjs4如何處理后臺(tái)json數(shù)據(jù)中日期和時(shí)間
- 01-10C#中DataGridView常用操作實(shí)例小結(jié)
- 01-10C#編程獲取資源文件中圖片的方法
- 01-10asp.net中XML如何做增刪改查操作
- 01-10C#利用反射技術(shù)實(shí)現(xiàn)去掉按鈕選中時(shí)的邊框效果
- 01-10C#中查找Dictionary中的重復(fù)值的方法
- 01-10C#中TreeView實(shí)現(xiàn)適合兩級(jí)節(jié)點(diǎn)的選中節(jié)點(diǎn)方法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開(kāi)的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻 器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫(xiě)Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10delphi制作wav文件的方法
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 04-02jquery與jsp,用jquery
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文