Ruby 异常

  • 异常

    执行和异常总是并存的。如果您打开的文件不存在,则您没有正确处理此情况,则被认为程序质量很差。如果发生异常,程序将停止。因此,异常用于处理各种类型的错误,这些错误可能在程序执行期间发生,并采取适当的措施而不是完全停止程序。Ruby提供了一种很好的机制来处理异常。我们将可能引发异常的代码括在begin/end块中,并使用救援子句告诉Ruby我们要处理的异常类型。
    异常
    
    begin  
    # -  
    rescue OneTypeOfException  
    # -  
    rescue AnotherTypeOfException  
    # -  
    else  
    # Other exceptions
    ensure
    # Always will be executed
    end
    
    从begin到rescue的一切都受到保护。如果在执行此代码块期间发生异常,则将控制权传递给rescue和end之间的块。对于begin块中的每个rescue子句,Ruby依次将引发的Exception与每个参数进行比较。如果rescue子句中命名的异常与当前抛出的异常的类型相同,或者是该异常的超类,则匹配将成功。如果异常与指定的任何错误类型都不匹配,则允许我们在所有救援子句之后使用else子句。
    示例
    
    begin
       file = open("/unexistant_file")
       if file
          puts "文件打开成功"
       end
    rescue
          file = STDIN
    end
    print file, "==", STDIN, "\n"
    
    这将产生以下结果。您会看到STDIN被替换为file,因为打开失败。
    
    #<IO:0xb7d16f84>==#<IO:0xb7d16f84>
    
  • 使用retry语句

    您可以使用rescue块捕获异常,然后使用retry语句从头开始执行begin块。
    语法
    
    begin
       # 此代码将引发的异常
       # 在rescue中给捕获到
    rescue
       # 此块将捕获所有类型的异常
       retry  # 这将把控制移动到begin的开头重新执行
    end
    
    这将产生以下结果-
    示例
    
    begin
       file = open("/unexistant_file")
       if file
          puts "File opened successfully"
       end
    rescue
       fname = "existant_file"
       retry
    end
    
    以下是上面代码流程-
    • open时发生异常。
    • 到rescue。fname已重新分配。
    • 通过重试begin了。
    • 这次文件成功打开。
    • 继续进行基本过程。
    – 请注意,如果重新命名的文件不存在,则此示例代码将无限重试。如果将重试用于异常过程,最终资源耗尽,程序崩溃,请小心。
  • 使用raise语句

    您可以使用raise语句引发异常。每当调用以下方法时,都会引发异常。这是第二条消息将被打印。
    语法
    
    raise 
    
    或者
    
    raise "Error Message" 
    
    或者
    
    raise ExceptionType, "Error Message"
    
    或者
    
    raise ExceptionType, "Error Message" condition
    
    • 第一种形式只是重新引发当前异常(如果没有当前异常,则引发RuntimeError)。它用于需要在传递异常之前拦截异常的异常处理程序。
    • 第二种形式创建一个新的RuntimeError异常,将其消息设置为给定的字符串。然后在调用堆栈中引发此异常。
    • 第三种形式使用第一个参数创建异常,然后将关联的消息设置为第二个参数。
    • 第四种形式与第三种形式相似,但是您可以添加任何条件语句,例如除非引发异常。
    示例
    
    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的用法-
    
    begin  
       raise 'A test exception.'  
    rescue Exception => e  
       puts e.message  
       puts e.backtrace.inspect  
    end    
    
    尝试一下
    这将产生以下结果-
    
    A test exception.
    ["Tmp5f2a5e4abd2161596612170.rb:2:in"]
    
  • 使用 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.
    ["Tmp5f2a5f302e4171596612400.rb:2:in"]
    Ensuring execution
    
  • 使用 else 语句

    如果else子句存在,它将在rescue子句之后并在任何ensure之前。eles代码块只有在没有异常是由代码的主体抛出异常时执行。
    语法
    
    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
    
  • 使用 throw 语句

    虽然引发异常的异常机制非常适合在出现错误时放弃执行,但是有时可以在正常处理过程中跳出一些深层嵌套的构造是很好的。这是throw...cathc派上用场的地方。所述捕获限定了标记的具有给定名称(其可以是符号或字符串)的块。该块将正常执行,直到遇到throw。
    语法
    
    throw :lablename
    #.. 这将不会被执行
    catch :lablename do
    #.. 匹配捕获将在遇到throw后执行。
    end
    
    或者
    
    throw :lablename condition
    #.. 这将不会被执行
    catch :lablename do
    #.. 匹配捕获将在遇到throw后执行。
    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: Hib
    Age: 3
    Sex: !
    Name: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
    
    这里重要的一行是引发FileSaveError.new($!)。我们调用raise表示发生了异常,并向其传递了FileSaveError的新实例,原因是特定的数据写入失败导致异常。