Ruby 教程
異常和執(zhí)行總是被聯(lián)系在一起。如果您打開一個(gè)不存在的文件,且沒有恰當(dāng)?shù)靥幚磉@種情況,那么您的程序則被認(rèn)為是低質(zhì)量的。
如果異常發(fā)生,則程序停止。異常用于處理各種類型的錯(cuò)誤,這些錯(cuò)誤可能在程序執(zhí)行期間發(fā)生,所以要采取適當(dāng)?shù)男袆?dòng),而不至于讓程序完全停止。
Ruby 提供了一個(gè)完美的處理異常的機(jī)制。我們可以在 begin/end 塊中附上可能拋出異常的代碼,并使用 rescue 子句告訴 Ruby 完美要處理的異常類型。
從 begin 到 rescue 中的一切是受保護(hù)的。如果代碼塊執(zhí)行期間發(fā)生了異常,控制會(huì)傳到 rescue 和 end 之間的塊。
對于 begin 塊中的每個(gè) rescue 子句,Ruby 把拋出的異常與每個(gè)參數(shù)進(jìn)行輪流比較。如果 rescue 子句中命名的異常與當(dāng)前拋出的異常類型相同,或者是該異常的父類,則匹配成功。
如果異常不匹配所有指定的錯(cuò)誤類型,我們可以在所有的 rescue 子句后使用一個(gè) else 子句。
以上實(shí)例運(yùn)行輸出結(jié)果為。您可以看到,STDIN 取代了 file ,因?yàn)?i>打開失敗。
#<IO:0xb7d16f84>==#<IO:0xb7d16f84>
您可以使用 rescue 塊捕獲異常,然后使用 retry 語句從開頭開始執(zhí)行 begin 塊。
以下是處理流程:
注意:如果被重新命名的文件不存在,本實(shí)例代碼會(huì)無限嘗試。所以異常處理時(shí),謹(jǐn)慎使用 retry。
您可以使用 raise 語句拋出異常。下面的方法在調(diào)用時(shí)拋出異常。它的第二個(gè)消息將被輸出。
第一種形式簡單地重新拋出當(dāng)前異常(如果沒有當(dāng)前異常則拋出一個(gè) RuntimeError)。這用在傳入異常之前需要解釋異常的異常處理程序中。
第二種形式創(chuàng)建一個(gè)新的 RuntimeError 異常,設(shè)置它的消息為給定的字符串。該異常之后拋出到調(diào)用堆棧。
第三種形式使用第一個(gè)參數(shù)創(chuàng)建一個(gè)異常,然后設(shè)置相關(guān)的消息為第二個(gè)參數(shù)。
第四種形式與第三種形式類似,您可以添加任何額外的條件語句(比如 unless)來拋出異常。
以上實(shí)例運(yùn)行輸出結(jié)果為:
I am before the raise. I am rescued. I am after the begin block.
另一個(gè)演示 raise 用法的實(shí)例:
以上實(shí)例運(yùn)行輸出結(jié)果為:
A test exception. ["main.rb:4"]
有時(shí)候,無論是否拋出異常,您需要保證一些處理在代碼塊結(jié)束時(shí)完成。例如,您可能在進(jìn)入時(shí)打開了一個(gè)文件,當(dāng)您退出塊時(shí),您需要確保關(guān)閉文件。
ensure 子句做的就是這個(gè)。ensure 放在最后一個(gè) rescue 子句后,并包含一個(gè)塊終止時(shí)總是執(zhí)行的代碼塊。它與塊是否正常退出、是否拋出并處理異常、是否因一個(gè)未捕獲的異常而終止,這些都沒關(guān)系,ensure 塊始終都會(huì)運(yùn)行。
以上實(shí)例運(yùn)行輸出結(jié)果為:
A test exception. ["main.rb:4"] Ensuring execution
如果提供了 else 子句,它一般是放置在 rescue 子句之后,任意 ensure 之前。
else 子句的主體只有在代碼主體沒有拋出異常時(shí)執(zhí)行。
以上實(shí)例運(yùn)行輸出結(jié)果為:
I'm not raising exception Congratulations-- no errors! Ensuring execution
使用 $! 變量可以捕獲拋出的錯(cuò)誤消息。
raise 和 rescue 的異常機(jī)制能在發(fā)生錯(cuò)誤時(shí)放棄執(zhí)行,有時(shí)候需要在正常處理時(shí)跳出一些深層嵌套的結(jié)構(gòu)。此時(shí) catch 和 throw 就派上用場了。
catch 定義了一個(gè)使用給定的名稱(可以是 Symbol 或 String)作為標(biāo)簽的塊。塊會(huì)正常執(zhí)行直到遇到一個(gè) throw。
下面的實(shí)例中,如果用戶鍵入 '!' 回應(yīng)任何提示,使用一個(gè) throw 終止與用戶的交互。
上面的程序需要人工交互,您可以在您的計(jì)算機(jī)上進(jìn)行嘗試。以上實(shí)例運(yùn)行輸出結(jié)果為:
Name: Ruby on Rails Age: 3 Sex: ! Name:Just Ruby
Ruby 的標(biāo)準(zhǔn)類和模塊拋出異常。所有的異常類組成一個(gè)層次,包括頂部的 Exception 類在內(nèi)。下一層是七種不同的類型:
Fatal 是該層中另一種異常,但是 Ruby 解釋器只在內(nèi)部使用它。
ScriptError 和 StandardError 都有一些子類,但是在這里我們不需要了解這些細(xì)節(jié)。最重要的事情是創(chuàng)建我們自己的異常類,它們必須是類 Exception 或其子代的子類。
讓我們看一個(gè)實(shí)例:
現(xiàn)在,看下面的實(shí)例,將用到上面的異常:
在這里,最重要的一行是 raise FileSaveError.new($!)。我們調(diào)用 raise 來示意異常已經(jīng)發(fā)生,把它傳給 FileSaveError 的一個(gè)新的實(shí)例,由于特定的異常引起數(shù)據(jù)寫入失敗。