位置:首頁 > 腳本語言 > Ruby教學 > Ruby異常

Ruby異常

執行和異常總是一起的。如果打開一個文件,但是它不存在,那麼如果冇有處理這種情況,那麼程序被認為是質量比較差的。

如果發生異常程序停止。因此,例外是用來處理不同類型的程序執行過程中可能發生的錯誤,並采取適當的處理動作,而不是完全停止程序。

Ruby提供了一個很好的機製來處理異常。我們附上的代碼,可以在 begin/end 塊拋出一個異常,並使用異常子句來告訴Ruby我們要處理的異常類型。

語法 :

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

一切從 rescue開始保護。這個代碼塊在執行過程中如果出現異常,控製傳遞給 rescue 和結束之間的塊。

對於每一個rescue 子句在BEGIN塊,Ruby依次對每個參數進行比較異常。rescue 子句中命名的異常是會成功的,如果目前拋出的異常的類型相同,或者是該異常的超類的匹配。

rescue 子句中命名的異常是會成功的,如果目前拋出的異常的類型相同,或者是該異常的超類的匹配。

例子:

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "
"

這將產生以下結果。我們可以看到 STDIN 替代文件,因為打開失敗。

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

retry 語句:

可以捕獲異常使用 rescue 塊,然後 retry 語句從頭開始執行 begin塊。 

語法:

begin
    # Exceptions raised by this code will 
	# be caught by the following rescue clause
rescue
    # This block will capture all types of exceptions
    retry  # This will move control to the beginning of begin
end

例子:

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

以下是處理的流程:

  • 發生異常打開的

  • 進入 rescue.  fname的重新分配

  • 從 begin 塊開始重試

  • 此時文件成功打開

  • 繼續過程

注意,如果重新替代的文件名稱不存在這個例子代碼無限重試。要小心,如果使用異常處理重試。

使用 raise 語句:

可以使用raise語句引發異常。下麵的方法拋出一個異常時它被調用。它的第二個消息將被打印。

語法:

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

第一形式簡單重新引發當前異常(或拋出一個RuntimeError,如果冇有當前異常)。這是用在需要攔截之前將它傳遞一個異常的異常處理程序。

第二種形式創建一個新的 RuntimeError 異常,設置它的消息給定的字符串。然後提出了此異常調用堆棧。

第三種形式使用的第一個參數創建一個異常,第二個參數設置相關的消息。

第四種形式是類似第三種形式的,但可以添加任何條件語句像 unlessto 來引發異常。

例子:

#!/usr/bin/ruby

begin  
    puts 'I am before the raise.'  
    raise 'An error has occurred.'  
    puts 'I am after the raise.'  
rescue  
    puts 'I am rescued.'  
end  
puts 'I am after the begin block.'  

這將產生以下結果:

I am before the raise.  
I am rescued.  
I am after the begin block.  

再舉一個例子來說明使用 raise:

#!/usr/bin/ruby

begin  
  raise 'A test exception.'  
rescue Exception => e  
  puts e.message  
  puts e.backtrace.inspect  
end  

這將產生以下結果:

A test exception.
["main.rb:4"]

使用 ensure 語句:

有時候需要做一些處理,無論是否拋出一個異常,要保證在一個代碼塊的結束。例如,可能有進入塊上打開的一個文件,需要確保它被關閉,退出塊。

ensure 子句確實能做這一點。ensure 在 rescue 子句的後麵,並包含一個代碼塊,將永遠被執行塊終止。如果塊退出正常,如果它會引發rescues 異常或者如果它是由未捕獲的異常終止,ensure 塊運行。

語法:

begin 
   #.. process 
   #..raise exception
rescue 
   #.. handle error 
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

實例:

begin
  raise 'A test exception.'
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
ensure
  puts "Ensuring execution"
end

這將產生以下結果:

A test exception.
["main.rb:4"]
Ensuring execution

使用 else 語句:

else 子句一般會在 rescue 語句之後,但在 ensure 語句之前。

else子句的主體,如果冇有拋出異常的主體代碼執行。

語法:

begin 
   #.. process 
   #..raise exception
rescue 
   # .. handle error
else
   #.. executes if there is no exception
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

實例:

begin
 # raise 'A test exception.'
 puts "I'm not raising exception"
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
  puts "Ensuring execution"
end

這將產生以下結果:

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

拋出的錯誤信息可以使用  $! 變量捕獲!

catch和throw:

雖然異常處理機製 raise 和 rescue 是非常適合放棄執行在出問題時,有時不錯,能夠跳出一些深層嵌套的結構,在正常處理。這是捕獲並拋出派上用場。

在catch定義一個塊的標記給定的名稱(這可能是一個符號或一個字符串)。該塊正常執行,直到遇到拋出。

語法:

throw :lablename
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

OR

throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

例子:

下麵的示例使用一個拋出來終止交互,用戶如果'!“鍵入的任何提示。

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # process information
end
promptAndGet("Name:")

嘗試上麵的程序在你的機器上,因為它需要手動交互。這將產生以下結果:

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

類異常:

Ruby的標準類和模塊引發異常。所有異常類形成了一個層次,在頂部類異常。下一個級彆包含六種不同類型:

  • Interrupt

  • NoMemoryError

  • SignalException

  • ScriptError

  • StandardError

  • SystemExit

還有另一個異常,在這個層麵上是致命的,但Ruby解釋器隻使用這個內部。

兩個 ScriptError 和 StandardError 有一些子類,但我們不需要在這裡詳談。最重要的是,如果我們創建我們自己的異常類,他們需要的任一類異常或者其子類。

讓我們來看一個例子:

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

現在來看看下麵的例子將使用此異常:

File.open(path, "w") do |file|
begin
    # Write out the data ...
rescue
    # Something went wrong!
    raise FileSaveError.new($!)
end
end

這裡最重要的行是提高 FileSaveError.new($!)。我們調用拋出發生了異常的信號,傳遞給它一個新的 FileSaveError的實例,是特定的異常導致寫入數據失敗的原因。