Ruby - 例外

执行和异常总是在一起.如果您打开一个不存在的文件,那么如果您没有正确处理这种情况,那么您的程序将被视为质量不佳.

如果异常,程序将停止发生.因此异常用于处理各种类型的错误,这些错误可能在程序执行期间发生,并采取适当的操作而不是完全停止程序.

Ruby提供了一种处理异常的好机制.我们附上可能在 begin/end 块中引发异常的代码,并使用 rescue 子句告诉Ruby我们想要处理的异常类型.

语法

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

开始救援的所有内容都受到保护.如果在执行此代码块期间发生异常,则控制将传递到 rescue end 之间的块.

For在 begin 块中的每个 rescue 子句中,Ruby依次将引发的Exception与每个参数进行比较.如果rescue子句中指定的异常与当前抛出的异常的类型相同,或者是该异常的超类,则匹配将成功.

在异常执行的情况下不匹配任何指定的错误类型,我们可以在所有 rescue 子句之后使用 else 子句.

示例

#!/usr/bin/ruby

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

这将产生以下结果.您可以看到 STDIN 被替换为 file ,因为 open 失败.

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

使用重试语句

您可以使用 rescue 块捕获异常,然后使用重试语句从头开始执行开始块.

语法

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 beginend

示例

#!/usr/bin/ruby

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

以下是流程的流程 :

  • 打开时发生异常.

  • 去救援. fname被重新分配.

  • 通过重试进入开始的开始.

  • 此时间文件成功打开.

  • 继续基本流程.

注意 : 请注意,如果重新替换名称的文件不存在,则此示例代码将无限重试.如果你使用重试进行异常处理,请小心.

使用raise语句

你可以使用 raise 声明提出异常.无论何时调用它,以下方法都会引发异常.它将打印第二条消息.

语法

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

第一种形式只是重新引发当前异常(如果没有当前异常,则返回RuntimeError) ).这用于需要在传递异常之前拦截异常的异常处理程序.

第二种形式创建一个新的 RuntimeError 异常,将其消息设置为给定串.然后在调用堆栈中引发此异常.

第三种形式使用第一个参数创建异常,然后将关联的消息设置为第二个参数.

第四种形式类似于第三种形式,但你可以添加任何条件语句,如,除非引发异常.

示例

#!/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"]

使用确保声明

有时候,你需要保证在代码块结束时完成某些处理,无论是否引发异常.例如,你可能在进入块时打开一个文件,你需要确保它在块退出时关闭.

确保子句只是这个. 确保在最后一个救援子句之后,并且包含一块代码,这些代码将在块终止时始终执行.如果块正常退出,如果它引发并拯救异常,或者如果它被未捕获的异常终止,那么确保块将被运行无关紧要.

语法

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 子句之后和任何确保之前.

只有在代码主体没有引发异常时才会执行 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

使用&dollar;可以捕获引发的错误消息!变量.

Catch and Throw

虽然加油和救援的异常机制非常适合在出现问题时放弃执行,但有时候很好能够在正常处理过程中跳出一些深层嵌套的构造.这就是catch和throw派上用场的地方.

catch 定义了一个用给定名称标记的块(可以是Symbol或String) .该块正常执行,直到遇到throw.

语法

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

示例

以下示例使用throw来终止与用户的交互,如果'! '是为响应任何提示而输入的.

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

Class Exception

Ruby的标准类和模块引发异常.所有异常类都形成一个层次结构,顶部有一个Exception类.下一级包含七种不同的类型 :

  • 中断

  • NoMemoryError

  • SignalException

  • ScriptError

  • StandardError

  • SystemExit

此级别还有另一个例外,致命,但Ruby解释器仅在内部使用它.

ScriptError和StandardError都有许多子类,但我们不需要在此处详细介绍.重要的是,如果我们创建自己的异常类,它们需要是Exception类或其后代之一的子类.

让我们看一个例子 :

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(&dollar;!)
end
end

这里重要的一行是raise FileSaveError.new(美元;!)的.我们调用raise来表示发生了异常,向它传递了一个新的FileSaveError实例,原因是该特定异常导致数据写入失败.