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