Ruby 異常
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 的一個新的例項,由於特定的異常引起資料寫入失敗。