Ruby異常

Ruby異常是一個對象,該類的異常或後代的實例。它代表一些特殊的條件。
在Ruby程序出現錯誤時,會引發異常行爲。 默認情況下,Ruby程序在拋出異常時終止。

我們可以在Ruby中聲明一些異常處理程序。 異常處理程序是一些代碼塊,當一些其他代碼塊發生異常時,該塊被執行。

異常的情況以兩種方式處理。 可以終止程序或處理異常。 如果要處理異常,可以提供一個rescue子句,這樣程序控制就可以流向rescue子句。

當異常提出但未處理時,全局變量$!包含當前異常,$@包含當前異常的回溯。

Ruby預定義的類,如Exception及其子項可以用來處理程序的錯誤。 在Ruby異常層次結構中,大多數子類都擴展了ClassError類。 這些是正常的異常。

Ruby類異常

異常的內置子類如下:

  • NoMemoryError
  • ScriptError
  • SecurityError
  • SignalException
  • StandardError
  • SystenExit
  • SystemStackError
  • fatal - 不可以拯救

示例

def raise_exception     
  puts 'I am before the raise.'     
  raise 'oops! An error has occured'     
  puts 'I am after the raise'     
end     
raise_exception

執行上面代碼,輸出結果如下 -

F:\worksp\ruby>ruby exception-raise.rb
I am before the raise.
exception-raise.rb:6:in `raise_exception': oops! An error has occured (RuntimeError)
        from exception-raise.rb:9:in `<main>'

F:\worksp\ruby>

raise方法來自內核模塊。

處理異常

爲了處理異常,引發異常的代碼包含在begin-end塊中。 使用rescue子句,我們可以說明我們要處理的異常類型。

示例:

#!/usr/bin/ruby   
#file: exception-raise_rescue.rb

def raise_and_rescue     
  begin     
    puts 'Before the raise.'     
    raise 'An error occured.'     
    puts 'After the raise.'     
  rescue     
    puts 'Code rescued.'     
  end     
  puts 'After the begin block.'     
end     
raise_and_rescue

執行上面代碼,得到以下結果 -

F:\worksp\ruby>ruby exception-raise_rescue.rb
Before the raise.
Code rescued.
After the begin block.

F:\worksp\ruby>

在上面的例子中,中斷的代碼不能完全運行。 異常處理代碼在開始結束塊(begin-end)之後恢復。

如果在rescue子句中未定義任何參數,則該參數默認爲StandardError。 每個rescue子句指定多個異常捕獲。 如果raise沒有使用任何參數,則可能會重新提出異常。

rescue子句寫在begin/rescue內,如果不是一個由rescue子句可處理的異常則將由下一個異常處理。

begin  
code..  
rescue OneTypeOfException  
code..  
rescue AnotherTypeOfException  
 code..  
else  
  # Other exceptions  
end

在開始(begin)塊中,每個具有引發異常的rescue子句將依次與每個參數進行比較。 當在rescue子句中指定的錯誤類型和異常名稱相同或是該異常的超類時,則將匹配它。 如果沒有異常,完成了begin語句的主體則執行else子句。 如果發生異常,則不會執行else子句。

Exception對象

異常對象是普通對象。rescue子句中的變量可以保存被拯救的異常。

示例:

begin   
  raise 'an exception'   
rescue ZeroDivisionError => e   
  puts "Exception Class: #{ e.class.name }"   
  puts "Exception Message: #{ e.message }"   
  puts "Exception Backtrace: #{ e.backtrace }"   
end

Exception類定義了兩種方法用於返回有關異常的詳細信息。 message方法返回一個定義錯誤說明的字符串。 backtrace方法返回一個字符串數組,表示在引發異常的時刻調用堆棧。

使用retry語句

通常在rescue子句中,捕獲異常並在begin塊阻止繼續執行代碼。 使用retry語句,捕獲塊代碼可以在捕獲異常之後還可以從begin塊處恢復。

語法:

begin  
   code....  
rescue  
    # capture exceptions  
    retry  # program will run from the begin block  
end

示例

#!/usr/bin/ruby   

begin   
   x = Dir.mkdir "alreadyExist"   
   if x   
      puts "Directory created"   
   end   
rescue   
   y = "newDir"   
   retry   
end

上述程序運行如下:

步驟1 - 在begin塊中,編寫代碼以創建一個已經存在的目錄。

步驟2 - 這將拋出一個錯誤。

步驟3 - 在rescue區,y被重新分配新值。

步驟4 - retry語句將轉到begin塊。

步驟5 - 將創建目錄。

使用raise語句

raise語句用於引發異常。

語法:

raise

或者 -

raise "Error Message"

或者 -

raise ExceptionType, "Error Message"

或者 -

raise ExceptionType, "Error Message" condition

第一個語法,重新引發當前的異常。 它用於異常處理程序,其中異常在傳遞之前被攔截。

第二個語法,創建一個新的RuntimeError異常。 然後這個異常被引發爲調用堆棧。

第三個語法,使用第一個參數創建一個異常,然後將關聯消息設置爲第二個參數。

第四個語法,類似第三個的語法。 在這裏可以添加任何條件語句來引發異常。

示例

#!/usr/bin/ruby   
# exceptions-raise-statement.rb

begin     
    puts 'code before raise.'     
    raise 'exception occurred.'     
    puts 'code after raise.'     
rescue     
    puts 'I am rescued.'     
end     
puts 'code after begin block.'

執行上面代碼,得到以下結果 -

F:\worksp\ruby>ruby exceptions-raise-statement.rb
code before raise.
I am rescued.
code after begin block.

F:\worksp\ruby>

使用ensure語句

有一個ensure子句,保證在代碼結尾處的一些處理。無論是否引發異常 ensure塊始終運行。 它放置在最後一個rescue子句之後,並且在塊的結束時執行。

無論是否發生異常,異常被rescue或代碼被未捕獲的異常終止,ensure塊都將運行。

語法

begin   
  code..  
   #..raise exception  
rescue   
   #.. exception is rescued  
ensure   
   #.. This code will always execute.  
end

示例代碼

#!/usr/bin/ruby   
# file : exception-ensure-statment.rb
begin   
  raise 'Exception'   
rescue Exception => e   
  puts e.message   
  puts e.backtrace.inspect   
ensure   
  puts "The ensure code will always run"   
end

執行上面示例代碼,得到以下結果 -

F:\worksp\ruby>ruby exception-ensure-statment.rb
Exception
["exception-ensure-statment.rb:5:in `<main>'"]
The ensure code will always run

F:\worksp\ruby>

使用else語句

else子句始終存在於rescue子句之後和before子句之前。 如果沒有引發異常,則只執行其他塊。

語法:

begin   
   code..   
   #..raise exception  
rescue   
   # .. exception is rescued  
else  
   #.. executes if there is no exception  
ensure   
   #..  This code will always execute.  
end

示例

#!/usr/bin/ruby   
# file : exception-else-statment.rb

begin   
 # raise 'A test exception.'   
 puts "no exception is raised"   
rescue Exception => e   
  puts e.message   
  puts e.backtrace.inspect   
else   
   puts "else code will be executed as no exception is raised."   
ensure   
  puts "ensure code will run"   
end

執行上面代碼,得到以下結果 -

F:\worksp\ruby>ruby exception-else-statment.rb
no exception is raised
else code will be executed as no exception is raised.
ensure code will run

F:\worksp\ruby>

Ruby catch和throw語句

Ruby catchthrow提供了一種在代碼中不需要進一步工作的同時從執行中跳出來的方法。

catch定義了一個標有給定名稱的塊。 它用於跳出嵌套代碼。 使用catch語句,塊將被正常執行,直到遇到throw

catchthrow方法比 rescueraise快。 因此,更適合使用。

語法:

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

或者 -

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

示例

#!/usr/bin/ruby   
# file : exception-catch-throw.rb

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

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

執行上面代碼,得到以下結果 -

F:\worksp\ruby>ruby exception-catch-throw.rb
Name: Maxsu
Occupation: Occupation
Name:Minsu

F:\worksp\ruby>