Ruby异常——为什么;否则;?
我试图理解Ruby中的异常,但我有点困惑。我使用的教程说,如果发生的异常与rescue语句识别的任何异常都不匹配,则可以使用“else”来捕获它:Ruby异常——为什么;否则;?,ruby,exception-handling,Ruby,Exception Handling,我试图理解Ruby中的异常,但我有点困惑。我使用的教程说,如果发生的异常与rescue语句识别的任何异常都不匹配,则可以使用“else”来捕获它: begin # - rescue OneTypeOfException # - rescue AnotherTypeOfException # - else # Other exceptions ensure # Always will be executed end 但是,我在后面的教程中还看到,在使用“rescue”
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
但是,我在后面的教程中还看到,在使用“rescue”时没有指定异常:
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
如果你能做到这一点,那么我还需要用别的吗?或者我可以在最后像这样使用通用的救援工具吗
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
rescue
# Other exceptions
ensure
# Always will be executed
end
else
用于当块完成而未引发异常时。无论块是否成功完成,确保运行
。例如:
begin
puts "Hello, world!"
rescue
puts "rescue"
else
puts "else"
ensure
puts "ensure"
end
这将打印
你好,世界
,然后else
,然后确保在开始救援结束块中的else
块在您可能期望发生某种异常时使用。如果您运行了所有预期的异常,但仍然没有引发任何异常,那么在else块中,您可以执行任何需要的操作,因为您知道原始代码运行时没有错误。下面是begin
表达式中的else
的具体用例。假设您正在编写自动测试,并且希望编写一个方法来返回由块引发的错误。但是,如果块没有引发错误,您也希望测试失败。您可以这样做:
def get_error_from(&block)
begin
block.call
rescue => err
err # we want to return this
else
raise "No error was raised"
end
end
请注意,您不能在开始
块内移动升高
,因为它将获得救援
d。当然,还有其他不使用else
的方法,比如在结束后检查err
是否为nil
,但这并不简洁
就我个人而言,我很少以这种方式使用else
,因为我认为它很少需要,但在那些罕见的情况下,它确实派上了用场
编辑
我想到了另一个用例。下面是一个典型的开始
/救援
:
begin
do_something_that_may_raise_argument_error
do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
handle_the_error
end
为什么这不太理想?因为它的目的是当做某件事可能会引起参数错误时拯救,而不是当做某件事时,前一行没有引起
通常最好使用begin
/rescue
来包装您想要保护的最小代码,以防上升
,否则:
- 您可以屏蔽代码中不应该出现的bug
- 救援的意图更难理解。有人(包括你未来的自己)可能会阅读代码并想知道“我想保护哪个表达式?它看起来像表达式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
对于else
块,我能看到的唯一原因是,如果您想在块之前执行某些操作,请确保开始
块中的代码没有引发任何错误
begin
puts "Hello"
rescue
puts "Error"
else
puts "Success"
ensure
puts "my old friend"
puts "I've come to talk with you again."
end
多亏了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
“教程是什么,所以我知道不推荐它吗?”Andrewgrim在谷歌搜索了一下之后,我想提问者遵循的教程是。奖金WTF:该教程的作者似乎剽窃了第一个例子(在那里它已经是不正确的),但更糟糕的是,他把注释的缩进搞砸了。是的,我认为引导人们远离它是明智的——这是一个多么伟大的无能和不诚实的组合!以防有人遇到同样的问题。在“Ruby Way.Third Edition”中,作者指出,begin
块中的else
子句用于解救前面的rescue
子句中未指定的类型的错误(基本上与本问题的教程所说的相同)。这是不正确的。这让我有一段时间感到困惑:在begin/end
块中,else
块仅在没有引发异常时运行。它不是“任何其他”例外的总括。对else
的需求非常少……通常,在进行任何救援之前,您只需将正在进行的非异常代码放入主begin
块。有关else
的一些合法的深奥用法,请参见后面的答案。为什么要在begin块中包含else部分?@AntarrByrd在Ruby中,begin
类似于其他语言中的try
。这里的else
意味着,如果在begin
(try
)块中没有抛出异常,则执行此操作。但是如果begin块中的代码没有抛出错误。您可以在那里继续,因为这是它将运行的唯一情况。@AntarrByrd有一个区别:异常处理程序将在中被禁用(在确保之前仍在运行)。为什么您不能在开始救援结束
块之后引发“未引发错误”
?这不是和在else
中执行完全相同吗?@Magne唯一有效的方法是,如果您也使用早期的返回
,即返回错误
,而不仅仅是错误
。如果没有,则该方法无法返回块的错误,因为在该方法的末尾有一个无条件提升。我通常尽量避免提前/显式返回
s(除了保护子句样式),因为它更容易遵循;这就是为什么else
语法更吸引人的原因。啊,我没有注意到begin-rescue-end
在一个要返回错误的方法中。但是,考虑我的问题,如果你不需要返回错误(例如,一个独立的代码>开始救援结束< /代码>),我猜如果你停止执行某件事,它将是相同的。