📜  Ruby-异常

📅  最后修改于: 2020-10-16 05:56:48             🧑  作者: Mango


执行和异常总是并存的。如果您打开的文件不存在,那么,如果您没有正确处理此情况,则认为程序质量很差。

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

Ruby提供了一种很好的机制来处理异常。我们将可能引发异常的代码封装在begin / end块中,并使用救援子句告诉Ruby我们要处理的异常类型。

句法

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

开始营救的一切都受到保护。如果在执行此代码块期间发生异常,则将控制权传递给rescueend之间的块。

对于begin块中的每个救援子句,Ruby依次将引发的Exception与每个参数进行比较。如果挽救子句中命名的异常与当前抛出的异常的类型相同,或者是该异常的超类,则匹配将成功。

如果异常与指定的任何错误类型都不匹配,则允许我们在所有救援子句之后使用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被替换为文件,因为打开失败。

#<0xb7d16f84>==#<0xb7d16f84>
0xb7d16f84>0xb7d16f84>

使用重试语句

您可以使用抢救块捕获异常,然后使用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

以下是流程的流程-

  • 打开时发生异常。
  • 去营救。 fname已重新分配。
  • 通过重试开始了。
  • 这次文件成功打开。
  • 继续了基本过程。

–请注意,如果重新命名的文件不存在,则此示例代码将无限重试。如果将试用于异常过程,请小心。

使用凸起语句

您可以使用引发语句引发异常。每当调用以下方法时,都会引发异常。这是第二条消息将被打印。

句法

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"]

使用保证声明

有时,您需要保证在代码块的末尾进行某些处理,而不管是否引发异常。例如,您可能在进入该块时打开了一个文件,并且需要确保该文件在该块退出时被关闭。

sure子句就是这样做的。确保在最后一个救援子句之后执行,并确保在块终止时将始终执行一小段代码。不管该块是否正常退出,引发并营救异常,或者被未捕获的异常终止,都可以确保确保块运行。

句法

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子句存在,它将在挽救子句之后并在任何确保之前。

仅当代码主体未引发异常时,才执行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定义了一个用给定名称(可以是Symbol或String)标记的块。该块将正常执行,直到遇到抛出。

句法

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的标准类和模块引发异常。所有异常类都构成一个层次结构,顶部是Exception类。下一级别包含七种不同的类型-

  • 打断
  • NoMemoryError
  • SignalException
  • ScriptError
  • 标准错误
  • 系统退出

在这个级别上还有另一个例外,即Fatal ,但是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($!)
end
end

这里重要的一行是引发FileSaveError.new(&dollar ;!) 。我们调用raise表示发生了异常,并向其传递了FileSaveError的新实例,原因是特定的异常导致数据写入失败。