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

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

.NET異常代碼編寫

2019-11-17 04:47:15
字體:
供稿:網(wǎng)友

  為什么使用異常(Exceptions)

  從錯誤代碼轉(zhuǎn)換到異常處理會對你的代碼風(fēng)格產(chǎn)生很大影響。要用一種不同的方法編寫程序,需要你培養(yǎng)一套新的編程習(xí)慣,在你開始努力這么做之前,這篇文章會讓你知道你的努力將非常有意義。

  錯誤代碼的使用已經(jīng)有相當(dāng)長的一段時間了。假如你在C++ 中編寫代碼,通常是下面的形式:

HRESULT retval = query.FetchRow();
if (FAILED(retval))
{
// error handling here
}
  使用錯誤代碼的真正問題是返回的代碼不是很好。盡管從理論上講,錯誤代碼可以正確處理程序中所有的錯誤,但總有一些問題是你無法預(yù)先知道的。一些傳統(tǒng)編程模型的脆弱性就是源于不正確的錯誤處理;編寫正確的代碼并不輕易.

  問題的癥結(jié)在于錯誤代碼這種方法要求程序員去完成人類不擅長的工作--任何時候的一致性和完整性,并且系統(tǒng)環(huán)境沒有給你任何幫助。異常(Exception)將使這一切變得輕易很多。

  是Opt Out而不是Opt In

  錯誤代碼和異常最重要的不同在于對"做正確的事情"的要求不一樣。錯誤代碼使用的是Opt in,假如代碼中對錯誤沒有做明確的處理,那么這個錯誤就會被忽略。

  而異常(Exceptions)使用的是Opt Out模型。在默認(rèn)情況下,運(yùn)行時(runtime)會根據(jù)這些異常(exceptions)做正確的處理。

  這個區(qū)別對編寫的代碼有著很重要的影響。因?yàn)檫\(yùn)行時(Runtime)能對問題進(jìn)行常規(guī)的處理,所以程序員就可以將精力放在那些需要非凡處理的問題。這可以使我們能用更少的精力卻得到更為健壯的代碼。光這一個好處就足以促成向異常(exception)的轉(zhuǎn)變,更何況異常(exception)還有一個重要的優(yōu)勢。

  更多的信息

  在編寫新代碼時,寫出的代碼不能正常工作是很常見的。你可能會從某個函數(shù)那得到諸如"0x80001345"類似的返回值,接下來你就不得不想辦法判定出其中的含義。

  你首先要做的就是把這個返回值翻譯成某種類型的符號代碼。雖然可能會有更好的辦法,但我通常是去查找 .h 文件中的常量。在找到之后,你需要打開MSDN來找出這代碼的含義,最后才能決定如何修改程序。這還是在你幸運(yùn)的情況下,有時候你更本就無法找到與錯誤碼對應(yīng)的符號代碼。

  有時候即使你找到了錯誤代碼,也沒有什么幫助。假如得到的錯誤碼是ERROR_BAD_ARGUMENTS,我知道這是由給出的某個參數(shù)不正確引起的,但我不知道是哪一個。我調(diào)用的那個函數(shù)雖然知道,但它無法告訴我是哪一個出了問題。

  有了異常機(jī)制,我除了能得到一個名字告訴我發(fā)生了哪一種錯誤,而且還能得到一個更具體的信息。例如,假如我傳遞了一個錯誤的參數(shù),exception會告訴我哪一個參數(shù)錯了。這節(jié)省了大量的時間。

  更好的是,exception信息還能給出提示,教你如何去解決問題。今年早些時候,我試著將Beta1 的代碼移植到Beta2中,當(dāng)我運(yùn)行程序的時候,程序拋出一個如下的異常(exception):

"Paint Operation cannot be performed on this thread. Use Control.Invoke() instead"

  升級了的Beta2代碼會檢查不正確的用法,假如找到了,就拋出一個異常(exception)并確切地告訴程序員如何去解決這個問題。要修改這么一個錯誤真的是很簡單。
  異常(exception)處理和性能

  當(dāng)異常(exception)處理機(jī)制加入到C++時,它由于減慢了代碼的執(zhí)行速度而沒得到好的聲譽(yù),使用了異常處理的代碼可能會比不使用的慢一點(diǎn)。有一些不好的說法是可以理解的,盡管應(yīng)該指出沒有使用異常處理的代碼會因?yàn)闆]有對眾多錯誤進(jìn)行檢查而經(jīng)常出問題。

  隨著時間的過去,C++中異常處理的開銷減少了,但并沒有減到零。為了恰當(dāng)?shù)貙?shí)現(xiàn)異常處理,C++編譯器必須在拋出異常(exception)的地方?jīng)Q定要創(chuàng)建哪些對象(這些對象使用完后需要釋放),然后生成代碼去檢查異常(exception),并在異常檢測完后正確地清理這些對象。這些代碼會產(chǎn)生副作用,加大了代碼優(yōu)化的難度,所以還是會為此而損失性能。只要是使用C++,異常(exception)就會影響性能。

  .NET世界使這一切對編譯器編寫者來說輕易了很多。運(yùn)行時(Runtime)對代碼的運(yùn)行了如指掌,.NET編譯器不必再去檢測異常的發(fā)生。更重要的是,對象能被垃圾收集器(garbage collector)所收集,所以不用再去分析對象的不同狀態(tài)。當(dāng)異常(exception)出現(xiàn)時,垃圾收集器會恰當(dāng)?shù)刈龊们謇砉ぷ鳌_@意味著在不出現(xiàn)異常(non-exceptional)的情況下不會有任何額外的開銷。

  使用異常處理(exception handling)

  我想我已經(jīng)讓你深信異常處理(exception handling)是一個好的思想,現(xiàn)在該到了了解如何去寫代碼實(shí)現(xiàn)異常處理的時候了。

  Throw
  當(dāng)一個異常情況出現(xiàn)時,它會被throw語句拋出。比如,假如一個函數(shù)需要一個非空字符串作為參數(shù),它可能會含有下列代碼:

public void PRocess(string location)
{
if (location == null)
throw new ArgumentNullException("null value", "location");
}
在這個例子中,用特定的信息和參數(shù)名創(chuàng)建了一個ArgumentNullException的新實(shí)例,并用throw語句將其拋出。

  Try-Catch

  編寫錯誤處理最基本的結(jié)構(gòu)就是try-catch??聪旅娴拇a:

try
{
 Process(currentLocation);
 Console.WriteLine("Done processing");
}
catch (ArgumentNullException e)
{
 // handle the exception here
}
  在這例子中,假如try代碼塊(本例中就是Process()函數(shù)) 中的代碼拋出一個ArgumentNullException異常,控制權(quán)就會馬上轉(zhuǎn)移到catch代碼段,而不去執(zhí)行Console.WriteLine()調(diào)用。
  更多通用的Catching

  在前面的例子中,catch語句捕捉了一個ArgumentNullException,它和Process()拋出的異常相匹配。但是這并不是必需的。一個catch語句能捕捉某指定的異常(exception)或任何從這個類繼續(xù)的異常(exceptions)。例如:


try
{
Process(currentLocation);
Console.WriteLine("Done processing");
}
catch (ArgumentException e)
{
// handle the exception here
}
  因?yàn)锳rgumentNullException是從ArgumentException繼續(xù)而來的,所以這個catch語句也能捕捉ArgumentNullException異常。另外,它還能捕捉其他繼續(xù)而來的異常:ArgumentOutOfRangeException、InvalidEnumArgumentException和

  DuplicateWaitObjectException。

  既然所有的異常(exceptions)最終都是從Exception類衍生出來的,那么只要對Exception類進(jìn)行捕捉,就能捕捉任何其他的異常(exception)。C++沒有限制用戶只能拋出Exception的導(dǎo)出類,C#提供了一種能捕捉所有異常的語法:

catch
{
// handle the exception here
}
  盡管這個語法適用于所有的異常,但它沒什么實(shí)際利用價值。大部分C++程序員會選擇拋出從Exception繼續(xù)而來的異常,即使他們不這么做,這個catch 語法也沒法讓你確定拋出的是什么異常。
  Catch語句的排列順序

  我們可以在一個try語句中放置多個catch語句,每個catch語句捕捉一種不同類型的異常。在前面這個例子中,對ArgumentException做一個非凡的處理是挺有必要的,對其它的異常可以進(jìn)行另外的處理。

  這個例子如下:

try
{
Process(currentLocation);
Console.WriteLine("Done processing");
}
catch (ArgumentException e)
{
 // handle the exception here
}
catch (Exception e)
{
 // handle the more general exception here
} 
  當(dāng)使用了多個catch語句時,導(dǎo)出類型必須列在它的任何基類之前。這有助于提高可讀性。你可以更早地判定出運(yùn)行時(Runtime)的行為。

  Catch Operations

  現(xiàn)在我們捕捉了一個異常(exception),希望用它做一些有用的事。我們要做的第一件事就是想把這個異常用一些額外的上下文信息包裝起來。

  我們用下面的方法來實(shí)現(xiàn):

try
{
 Process(currentLocation);
 Console.WriteLine("Done processing");
}
catch (ArgumentException e)
{
 throw new ArgumentException("Error while processing", e);
}
  這里使用了ArgumentException的構(gòu)造器,它接受了一個信息和一個異常。構(gòu)造器把傳遞給它的這個異常用一個新的異常包裝了起來并拋出。

  這個過程給開發(fā)者們提供了一種極大的便利。將異常包裝起來就得到了類似于堆棧跟蹤(stack trace)的結(jié)果,而不僅僅是一些關(guān)于異常的單一信息:

System.Exception: Exception in Test1
---> System.Exception: Exception in Test2
---> System.DivideByZeroException: Attempted to divide by zero.
  假如你在編譯時使用了/debug開關(guān),這樣的輸出將使大大方便調(diào)試。你還可以得到每一級的文件名和行號。

  異常(exceptioin)的包裝在為調(diào)試提供額外的信息方面很有用。另外一種很有幫助的場合就是在你需要根據(jù)一個異常采取相應(yīng)的行動的時候。把輸出寫入一個文件的實(shí)現(xiàn)代碼應(yīng)該像下面這樣:

try
{
 FileStream f = new FileStream(filename, FileMode.Create);
 StreamWriter s = new StreamWriter(f);
 s.WriteLine("{0} {1}", "test", 55);
 s.Close();
 f.Close();
}
catch (IOException e)
{
 Console.WriteLine("Error opening file {0}", filename);
 Console.WriteLine(e);
}
  假如這個文件不能被打開,系統(tǒng)就拋出一個異常,catch語句就被觸發(fā),產(chǎn)生一個錯誤,程序可繼續(xù)執(zhí)行下去。在大多數(shù)情況下,這是沒問題的。 但是當(dāng)出現(xiàn)下面這種情況時問題就來了:一個異常出現(xiàn)在這個文件剛被打開后,這樣該文件將無法被關(guān)閉,這是有害的。

  這里需要的是一種能保證即使在異常發(fā)生的情況下,文件仍能被關(guān)閉的方法。 QQread.com 推出各大專業(yè)服務(wù)器評測 linux服務(wù)器的安全性能
SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器
  Try-Finally

  Finally 這個結(jié)構(gòu)用來指定一段代碼,即使在異常發(fā)生的情況下,這段代碼也能夠保證運(yùn)行。通常,finally語句用來負(fù)責(zé)在異常發(fā)生后的清理工作。我們把前面的例子修改如下:

try
{
 FileStream f = new FileStream(filename, FileMode.Create);
 StreamWriter s = new StreamWriter(f);

 s.WriteLine("{0} {1}", "test", 55);
 s.Close();
 f.Close();
}
catch (IOException e)
{
 Console.WriteLine("Error opening file {0}", filename);
 Console.WriteLine(e);
}
finally
{
 if (f != null)
  f.Close();
}
  在這段修改過的代碼中,即使發(fā)生了異常,finally語句也將被執(zhí)行。

  Using和Lock語句

  前面我們看到的是一個非經(jīng)常用的模式,C#提供了一種非凡的支持以促使程序員們寫出正確的代碼。Using語句可以被用來創(chuàng)建類似try-finally的代碼:

using (FileStream f = new FileStream(filename, FileMode.Create))
{
 StreamWriter s = new StreamWriter(f);

 s.WriteLine("{0} {1}", "test", 55);
 s.Close();
 f.Close();
}
catch (IOException e)
{
 Console.WriteLine("Error opening file {0}", filename);
 Console.WriteLine(e);
}
  這段代碼等同于前面的例子。

  Lock語句提供了一個類似的對System.Threading.Monitor類的包裝,以保證即使在異常發(fā)生的情況下鎖(locks)也能被釋放。 QQread.com 推出各大專業(yè)服務(wù)器評測 Linux服務(wù)器的安全性能 SUN服務(wù)器 HP服務(wù)器 DELL服務(wù)器 IBM服務(wù)器 聯(lián)想服務(wù)器 浪潮服務(wù)器 曙光服務(wù)器 同方服務(wù)器 華碩服務(wù)器 寶德服務(wù)器
  設(shè)計方針

  這個部分將講述一些異常處理的設(shè)計方針。

  1、盡量捕捉明確的異常

   假如你的代碼需要從一些異常中恢復(fù)過來,則要確信只捕捉那些異常。假如你捕捉更多的通用異常,這就與你錯誤地忽略這些異常沒什么區(qū)別。

  2、只忽略你有把握的異常

   這是由前面那個方針推斷而出的。當(dāng)你忽略一個異常時,意味著你應(yīng)該知道這個異常能引起的所有可能情況,并且你編寫的恢復(fù)代碼能夠處理所有的這些情況。

  3、假如合適的話,盡量使用lock或using語句

   假如你能使用lock或using 語句,就盡量使用它們。它們使代碼更具可讀性,而且能使得你更少犯錯誤。
  4、假如可能的話,盡量包裝異常

   假如你可以往一個異常中加入額外的信息的話,要盡一切辦法做到。假如我要往另一個函數(shù)傳遞一個參數(shù),那么加入這個參數(shù)的額外信息對我來說是很有幫助的。

try
{
 o.Process(v1, v2, v3);
 }
catch (ArgumentException e)
 {
  throw new ArgumentException("error updating contact information", e);
 }
  Catch語句將捕捉到的表達(dá)式(eXPression)用同樣的類型包裝起來并拋出。這給了用戶兩個層次的信息。

  然而使用這種方法時要當(dāng)心,在這個例子中,catch語句能夠捕捉ArgumentException異常和任何從這個類繼續(xù)而來的異常(比如ArgumentNullException)。但我的調(diào)用者從來不會關(guān)心這些區(qū)別,只是拋出一個ArgumentException異常。

  這種方法的最壞情況就是將一個Exception這個異常包裝并再次拋出。假如調(diào)用者需要對這個異常的真實(shí)類型進(jìn)行解碼,則必需用下面的代碼:


try
{
 o.BadFunction();
}
catch (Exception e)
{
 if (e.Inner.GetType() == typeof(ArgumentException))
 {

 }
 else
  throw;
}
  這段代碼檢查每個被包裝了的異常,要么處理它要么再次拋出它。假如你的用戶不得不編寫類似的代碼,那么你的代碼將受到嚴(yán)重影響。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 偃师市| 武邑县| 长沙县| 霍城县| 丽水市| 冕宁县| 扬中市| 义乌市| 桓台县| 潞城市| 土默特右旗| 海口市| 丰原市| 酒泉市| 昌平区| 榆树市| 大化| 南溪县| 太湖县| 和林格尔县| 黄浦区| 怀来县| 无棣县| 育儿| 汶上县| 遵义市| 汕尾市| 广州市| 邵阳市| 保山市| 高阳县| 克山县| 海阳市| 尖扎县| 荔浦县| 庄浪县| 宁陕县| 德州市| 随州市| 祁东县| 巴林左旗|