- Ruby 基础
- Ruby - 首页
- Ruby - 概述
- Ruby - 环境设置
- Ruby - 语法
- Ruby - 类和对象
- Ruby - 变量
- Ruby - 运算符
- Ruby - 注释
- Ruby - IF...ELSE
- Ruby - 循环
- Ruby - 方法
- Ruby - 代码块
- Ruby - 模块
- Ruby - 字符串
- Ruby - 数组
- Ruby - 哈希表
- Ruby - 日期和时间
- Ruby - 范围
- Ruby - 迭代器
- Ruby - 文件 I/O
- Ruby - 异常
Ruby - 异常
执行和异常总是紧密相连的。如果您打开一个不存在的文件,那么如果您没有正确处理这种情况,那么您的程序就被认为质量不高。
如果发生异常,程序就会停止。因此,异常用于处理程序执行期间可能发生的各种类型的错误,并采取适当的措施,而不是完全停止程序。
Ruby 提供了一种很好的机制来处理异常。我们将可能引发异常的代码包含在begin/end块中,并使用rescue子句来告诉 Ruby 我们想要处理的异常类型。
语法
begin # - rescue OneTypeOfException # - rescue AnotherTypeOfException # - else # Other exceptions ensure # Always will be executed end
从begin到rescue的所有内容都受到保护。如果在此代码块的执行过程中发生异常,则控制权将传递到rescue和end之间的块。
对于begin块中的每个rescue子句,Ruby 依次将引发的异常与每个参数进行比较。如果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>
使用 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
以下是流程:
- 在 open 处发生异常。
- 进入 rescue。fname 被重新赋值。
- 通过 retry 返回到 begin 的开头。
- 这次文件成功打开。
- 继续执行主要流程。
注意 - 请注意,如果重新替换名称的文件不存在,此示例代码将无限重试。如果您对异常处理使用retry,请小心。
使用 raise 语句
您可以使用raise语句引发异常。以下方法在每次被调用时都会引发异常。它的第二个消息将被打印。
语法
raise OR raise "Error Message" OR raise ExceptionType, "Error Message" OR raise ExceptionType, "Error Message" condition
第一种形式只是重新引发当前异常(如果没有当前异常,则引发 RuntimeError)。这用于需要在传递异常之前拦截异常的异常处理程序。
第二种形式创建一个新的RuntimeError异常,将其消息设置为给定的字符串。然后此异常向上抛到调用堆栈。
第三种形式使用第一个参数创建异常,然后将关联的消息设置为第二个参数。
第四种形式类似于第三种形式,但您可以添加任何条件语句,如unless,来引发异常。
示例
#!/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子句之后,并包含一段代码,这段代码将在块终止时始终执行。无论块是否正常退出、是否引发并捕获了异常,或者是否被未捕获的异常终止,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
可以使用$!变量捕获引发的错误消息。
捕获和抛出
虽然 raise 和 rescue 的异常机制非常适合在出现问题时放弃执行,但在正常处理过程中能够跳出某些深度嵌套的结构有时也很不错。这就是 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
Exception 类
Ruby 的标准类和模块会引发异常。所有异常类都形成一个层次结构,以 Exception 类位于顶部。下一层包含七种不同的类型:
- Interrupt
- NoMemoryError
- SignalException
- ScriptError
- StandardError
- SystemExit
在这一层还有一个异常,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
这里的重要行是 raise FileSaveError.new($!)。我们调用 raise 来表示发生了异常,并向它传递一个 FileSaveError 的新实例,原因是导致数据写入失败的特定异常。