【问题标题】:Ruby Exceptions -- Why "else"?Ruby 异常——为什么是“else”?
【发布时间】:2011-09-10 22:03:03
【问题描述】:

我试图理解 Ruby 中的异常,但我有点困惑。我正在使用的教程说,如果发生与救援语句标识的任何异常都不匹配的异常,您可以使用“else”来捕获它:

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

但是,我在教程后面也看到“救援”被使用,没有指定异常:

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

如果你能做到这一点,那么我是否需要使用 else ?或者我可以像这样在最后使用通用救援吗?

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

【问题讨论】:

  • 教程是什么,不知道不推荐?
  • @AndrewGrimm 经过一番谷歌搜索,我认为提问者所遵循的教程是tutorialspoint.com/ruby/ruby_exceptions.htm。额外的 WTF:该教程的作者似乎抄袭了rubylearning.com/satishtalim/ruby_exceptions.html 的第一个示例(它已经不正确),但通过搞砸了 cmets 的缩进使情况变得更糟。是的,我认为引导人们远离它是明智的 - 无能不诚实的完美结合!
  • 以防万一有人遇到同样的问题。在“The Ruby Way. 第三版”中,作者声明begin 块内的else 子句用于挽救前面rescue 子句中未指定类型的错误(与本教程中的教程基本相同)问题说)。这是不正确的。这让我困惑了一段时间
  • 要明确:在begin/end 块中,else 块仅在没有引发异常时运行。它不是“任何其他”异常的包罗万象。对else 的需求非常罕见……通常,您只需将正在进行的、非异常的代码放在主begin 块中,然后再进行任何救援。有关else 的一些合法深奥用法,请参阅稍后的答案。

标签: ruby exception-handling


【解决方案1】:

这是elsebegin 表达式中的具体用例。假设您正在编写自动化测试,并且您想编写一个返回块引发的错误的方法。但是,如果块没有引发错误,您也希望测试失败。你可以这样做:

def get_error_from(&block)
  begin
    block.call
  rescue => err
    err  # we want to return this
  else
    raise "No error was raised"
  end
end

请注意,您不能在begin 块内移动raise,因为它会得到rescued。当然,还有其他不使用else的方法,比如检查errend之后是否为nil,但不是那么简洁。

就个人而言,我很少以这种方式使用else,因为我认为它很少需要,但在极少数情况下它确实会派上用场。

编辑

我想到了另一个用例。这是一个典型的begin/rescue

begin
  do_something_that_may_raise_argument_error
  do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
  handle_the_error
end

为什么这不太理想?因为当do_something_that_may_raise_argument_error 引发ArgumentError 时,意图是rescue,当do_something_else_when_the_previous_line_doesnt_raise 引发时不是

通常最好使用begin/rescue 将要保护的最小代码封装在raise 中,否则:

  • 你可以掩盖代码中不应该出现的错误raise
  • rescue 的意图更难破译。有人(包括你未来的自己)可能会读到代码并想知道“我想保护哪个表达式?它看起来像表达式 ABC...但也可能是表达式 DEF????作者是什么?故意的?!”重构变得更加困难。

您可以通过这个简单的更改避免这些问题:

begin
  do_something_that_may_raise_argument_error
rescue ArgumentError => e
  handle_the_error
else
  do_something_else_when_the_previous_line_doesnt_raise
end

【讨论】:

  • 为什么你不能在begin rescue end 块之后直接raise "No error was raised"?这和在else 里面做的不完全一样吗?
  • @Magne 唯一可行的方法是如果您使用早期的return,即return err,而不仅仅是err。如果不这样做,则该方法无法返回块的错误,因为您在方法末尾有一个无条件的 raise。我通常会尽量避免提早/明确的returns(除了保护条款样式),因为它更容易理解;这就是为什么else 语法更吸引人的原因。
  • 啊,我没有注意到begin rescue end 在您要返回错误的方法中。但是,如果您不需要返回错误(即独立的begin rescue end),请考虑我的问题。
  • 我想只有在救援块中停止执行(例如重新引发错误)时,它才会是相同的。在那种情况下, else 将是多余的。
【解决方案2】:

感谢else,您有时可以合并两个嵌套的begin end 块。
所以(我当前代码中的简化示例)而不是:

  begin
    html = begin
      NetHTTPUtils.request_data url
    rescue NetHTTPUtils::Error => e
      raise unless 503 == e.code
      sleep 60
      retry
    end
    redo unless html["market"]
  end

你写:

  begin
    html = NetHTTPUtils.request_data url
  rescue NetHTTPUtils::Error => e
    raise unless 503 == e.code
    sleep 60
    retry
  else
    redo unless html["market"]
  end

【讨论】:

    【解决方案3】:

    我能看到else 块的唯一原因是,如果您想在ensure 块之前执行某些操作,而begin 块中的代码没有引发任何错误。

    begin
      puts "Hello"
    rescue
      puts "Error"
    else
      puts "Success"
    ensure
      puts "my old friend"
      puts "I've come to talk with you again."
    end
    

    【讨论】:

      【解决方案4】:

      else 用于当块完成时没有抛出异常。无论块是否成功完成,都会运行ensure。示例:

      begin
        puts "Hello, world!"
      rescue
        puts "rescue"
      else
        puts "else"
      ensure
        puts "ensure"
      end
      

      这将打印Hello, world!,然后是else,然后是ensure

      【讨论】:

      • 为什么在开始块中包含 else 部分?
      • @AntarrByrd 在 Ruby 中,begin 就像在其他语言中的 tryelse 在这里的意思是,如果在 begin (try) 块中没有引发异常,则执行此操作。
      • 但是如果开始块中的代码没有抛出错误。您可以在那里继续,因为这是它运行的唯一情况。
      • @AntarrByrd 有一个区别:异常处理程序将在 else 中被禁用(同时仍在 ensure 之前运行)。
      【解决方案5】:

      begin rescue end 块中的else 块用于当您可能期望发生某种异常时。如果您运行了所有预期的异常但仍然没有引发任何异常,那么在您的 else 块中您可以做任何需要的事情,因为您知道您的原始代码运行时没有错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-13
        • 1970-01-01
        • 2015-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多