`raise "foo"`和`raise Exception.new("foo")`有什么区别?

105

在技术上、哲学上、概念上或其他方面,这些之间有什么不同呢?

raise "foo"

and

raise Exception.new("foo")

?

2个回答

128

从技术上讲,第一个会引发一个RuntimeError,消息内容为"foo",而第二个会引发一个Exception,消息内容也为"foo"

实际上,在你想使用前者和后者之间有很大的区别。

简单来说,你可能需要一个RuntimeError而不是Exception。没有参数的rescue块可以捕获RuntimeErrors,但不会捕获Exceptions。所以如果你在代码中引发了一个Exception,这段代码将无法捕获它:

begin
rescue
end

要捕获Exception,您需要执行以下操作:

begin
rescue Exception
end
这意味着在某种意义上,ExceptionRuntimeError更严重,因为您需要更多的工作来从中恢复过来。
因此,您想要哪种错误取决于项目如何处理错误。例如,在我们的守护进程中,主循环具有一个空的rescue块,可以捕获RuntimeErrors、报告错误,然后继续执行。但在一两种情况下,我们希望守护进程真的遇到错误时能够立即停止运行,在这种情况下,我们会引发一个Exception,它会直接通过我们的“常规错误处理代码”并退出。
另外,如果您正在编写库代码,则可能需要RuntimeError而不是Exception,因为您的库的用户将感到惊讶,如果它引发了一个空的rescue块无法捕获的错误,并且他们需要一段时间才能意识到原因所在。
最后,我应该说RuntimeErrorStandardError类的子类,实际规则是尽管您可以raise任何类型的对象,但空的rescue默认只会捕获任何继承自StandardError的错误。其他所有类型都必须是具体的类型。

2
非常有用的信息,谢谢。几个要点:[1] 最后一段是最有启发性的,让我在irb上发现了你没有提到的东西:RuntimeError < StandardError < Exception [2] 因此,第二个代码块将捕获Exception和RuntimeError [3] 有趣/奇怪的是,“裸”raise和rescue恰好可以使用该特定异常 [4] 或许经验法则是将RuntimeError抛给客户端代码,但在自己的代码中引发和捕获自定义异常? - John Bachir
1
[1, 2] 是的。 [3] 不确定... [4] 当我以最专业的方式编写代码时,我倾向于创建从 StandardError 继承的自定义错误类型。它不必比 class MissingArgumentsError < StandardError; end 更复杂。 - Daniel Lucraft
非常有信息量,但在什么情况下,您会选择抛出异常而不是运行时错误,即使运行时错误更适合编写库? - Chihung Yu

35

官方文档说明如下:

raise   
raise( string )
raise( exception [, string [, array ] ] )

当没有参数时,会抛出$!中的异常,如果$!为nil,则抛出RuntimeError异常。当传入单个String参数时,它将抛出一个带有该字符串作为消息的RuntimeError异常。否则,第一个参数应该是一个Exception类的名称(或发送异常返回Exception对象的对象)。可选的第二个参数设置与异常相关联的消息,第三个参数是回调信息的数组。异常由begin...end块的rescue子句捕获。

raise "Failed to create socket"
raise ArgumentError, "No parameters", caller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接