【发布时间】:2010-01-07 04:03:04
【问题描述】:
我真的不认为这些有合理的用途。已经有rescue 和raise,那为什么还需要throw 和catch?似乎它们应该被用来跳出深巢,但这对我来说就像一个goto。有没有什么好的、干净的使用这些的例子?
【问题讨论】:
标签: ruby
我真的不认为这些有合理的用途。已经有rescue 和raise,那为什么还需要throw 和catch?似乎它们应该被用来跳出深巢,但这对我来说就像一个goto。有没有什么好的、干净的使用这些的例子?
【问题讨论】:
标签: ruby
注意: 1.9 中的 catch/throw 似乎发生了一些变化。这个答案适用于 Ruby 1.9。
一个很大的不同是你可以throw 任何东西,而不仅仅是从StandardError 派生的东西,不像raise。像这样的傻事是合法的,例如:
throw Customer.new
但这并不是很有意义。但你不能这样做:
irb(main):003:0> raise Customer.new
TypeError: exception class/object expected
from (irb):3:in `raise'
from (irb):3
from /usr/local/bin/irb:12:in `<main>'
【讨论】:
它们在为最终用户简化 DSL 方面非常有用,可以将控制权从 DSL 中传递出去,而无需复杂的 case/if 语句
我有一个 Ruby 应用程序,它允许用户通过内部 DSL 对其进行扩展。 DSL 中的某些功能需要将控制权返回给我的应用程序的特定部分。让我们举一个简单的例子。假设用户正在开发一个关于日期的简单扩展
if today is a holiday then
do nothing
end
week_of_year = today.week.number
if week_of_year < 10 then
...
do nothing 位触发了一个 throw,它将控制权从 exec 语句中传递给我。
在某些情况下,我们希望它退出并将控制权交还给我的应用程序,而不是继续执行 DSL。现在您可以让用户使用大量嵌入的 if 语句并让 DSL 自然结束,但这只会掩盖逻辑想要表达的意思。
Throw 确实是一个“被认为很危险”的 goto,但该死的,有时它们是最好的解决方案。
【讨论】:
它基本上是一个 goto,有点类似于 call/cc,除了控制流是通过名称隐式连接的,而不是作为参数显式连接。 throw/catch 和 raise/rescue 的区别在于前者旨在用于控制流而不是仅用于异常情况,并且不会浪费时间整理堆栈跟踪。
Sinatra 使用 throw/catch 处理 HTTP 错误代码,其中处理程序可以使用 throw 以结构化的方式将控制权交给 Sinatra 库。其他类型的 HTTP 框架使用异常,或者通过返回不同类的响应,但这让 Sinatra(例如)在捕获它之后尝试另一个请求处理程序。
【讨论】:
两者的区别在于你只能“引发”异常,但可以“抛出”任何东西(1.9)。除此之外,它们应该是可互换的,也就是说,应该可以相互重写,就像@john-feminella 给出的例子一样。
【讨论】: