Ryan Davis的Ruby QuickRef说(没有解释):
Don’t rescue Exception. EVER. or I will stab you.
为什么不?正确的做法是什么?
- 我知道答案,我只是希望有人能写出一个好的答案,因为我找不到一个好的答案需要几分钟的时间。到目前为止,没有一个答案是真正正确的。
- 那么你可能会自己写?:)
- 我对这里的暴力呼吁感到非常不安。只是编程而已。
- 看看RubyException中的这篇文章,它有一个很好的RubyException层次结构。
- 因为Ryan Davis会刺伤你。所以孩子们。永远不要拯救例外。
- @达特希格雷戈斯,我真的不知道你是不是在开玩笑。但我觉得很搞笑。(这显然不是严重的威胁)。现在每当我想到要抓住例外的时候,我都会考虑是否值得被网络上的某个随机的家伙刺伤。
tl;dr:用StandardError代替一般的异常捕获。当重新引发原始异常时(例如,当救援只记录异常时),救援Exception可能是正常的。
Exception是Ruby异常层次结构的根,所以当你rescue Exception时,你可以从任何东西中拯救出来,包括SyntaxError、LoadError和Interrupt等子类。
挽救Interrupt会阻止用户使用ctrlc退出程序。
挽救SignalException会阻止程序正确响应信号。它将是不可破解的,除非是由kill -9。
拯救SyntaxError意味着失败的evals将默默地这样做。
所有这些都可以通过运行该程序并尝试ctrlc或kill来显示:
1 2 3 4 5 6 7 8
| loop do
begin
sleep 1
eval"djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts"I refuse to fail or be stopped!"
end
end |
从Exception中拯救甚至不是违约。做
1 2 3 4 5
| begin
# iceberg!
rescue
# lifeboats
end |
不救Exception,救StandardError。一般来说,您应该指定比默认的StandardError更具体的内容,但是从Exception中进行救援会扩大范围,而不是缩小范围,并且可能会产生灾难性的结果,并使查找bug极其困难。
如果您希望从StandardError中进行救援,并且需要一个例外的变量,则可以使用此表单:
1 2 3 4 5
| begin
# iceberg!
rescue => e
# lifeboats
end |
相当于:
1 2 3 4 5
| begin
# iceberg!
rescue StandardError => e
# lifeboats
end |
从Exception中解救出来是明智的少数常见情况之一,是为了记录/报告的目的,在这种情况下,您应该立即重新提出例外:
1 2 3 4 5 6
| begin
# iceberg?
rescue Exception => e
# do some logging
raise e # not enough lifeboats ;)
end |
- 这就像在Java中捕获EDCOX1 0
- 您对这个用法有什么看法:stackoverflow.com/a/766228/513739
- @神剑如果你提出了例外,那么这是好的,因为你没有吞下它,但只是试图知道它发生了,然后让它泡沫起来。通常用于日志记录。
- 这个建议对于一个干净的Ruby环境很好。但不幸的是,许多gem已经创建了直接从异常下降的异常。我们的环境有30个:例如openid::server::encodingerror、oauth::invalidrequest、htmltokenizersample。这些都是你非常想在标准的救援区中抓住的例外。不幸的是,Ruby中没有任何东西可以阻止或甚至阻止gems直接从异常继承——即使命名是非结构化的。
- @Jonathanswartz然后从这些特定的子类中拯救出来,不例外。更具体的几乎总是更好更清楚。
- 安德鲁-很多时候你都想抓住所有的标准异常。你自己也提到过一个-如果你想在信息中添加一些上下文,那么重新显示、记录或者用气制动它。
- 我真的用ctrl+c杀了它
- @Jonathanswartz-我会让宝石创造者改变他们的异常继承自何处。就我个人而言,我喜欢我的宝石让所有的例外都来自我的GemeException,所以如果你愿意的话,你可以拯救它。
- 你也可以先是ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception],然后是rescue *ADAPTER_ERRORS => e。
- 我刚刚发现了另一个例子,为什么rescue Exception是坏的:它挽救了我们规范中的失败!我们的代码库中甚至有一个should_not_receive,作者显然信任这些规范和思想,它已经实现了:但实际上调用了这个方法,只是挽救了失败:(
- 所以,现在我只需要确保我们的gems不会引发异常或者某个超级duper自定义异常,这是异常的直接子类!
- 只有在不重新引发异常的情况下,挽救异常才是坏的,否则,它是在崩溃之前记录错误的好方法。
真正的规则是:不要丢弃异常。你方报价的作者的客观性是有问题的,事实证明它以
or I will stab you
当然,请注意,信号(默认情况下)抛出异常,并且通常长时间运行的进程是通过一个信号终止的,因此捕获异常而不在信号异常上终止会使程序很难停止。所以不要这样做:
1 2 3 4 5 6 7 8 9 10
| #! /usr/bin/ruby
while true do
begin
line = STDIN.gets
# heavy processing
rescue Exception => e
puts"caught exception #{e}! ohnoes!"
end
end |
不,真的,别这样。甚至不要运行它来查看它是否工作。
但是,假设您有一个线程服务器,并且您希望所有异常不:
被忽略(默认)
停止服务器(如果你说thread.abort_on_exception = true,就会发生这种情况)。
那么这在您的连接处理线程中是完全可以接受的:
开始做某事救援异常=>Emylogger.error("处理连接时未捕获e异常:e.message")mylogger.error("堆栈跟踪:backtrace.map l"l
- 对不起,这是错误的。服务器永远不应该挽救异常,只做日志记录。这将使它无法解决,除非由江户十一〔五〕号。
- 答案修正了。
- 您在注释3中的示例并不相同,无论是否引发异常,都将运行ensure,而rescue仅在引发异常时运行。
- 它们不是/完全是/等价的,但我不知道如何以一种不难看的方式简洁地表达等价。
- 只需在第一个示例中的begin/rescue块之后添加另一个关键的清理调用。我不同意最优雅的代码,但是很明显第二个例子是优雅的方式,所以有点不雅只是例子的一部分。
- "甚至不要运行它来查看它是否工作。"对于编码来说似乎是一个坏建议…相反,我建议你运行它,看到它失败,自己去理解如果失败怎么办,而不是盲目地相信别人。好答案:)
- "The objectivity of the author of your quote is questionable"。这家伙写了迷你测试和大量其他广泛使用的宝石。blog.zensider.com/projects/项目
假设你在一辆车里(开着红宝石)。您最近安装了一个新的方向盘和空中升级系统(使用eval),但您不知道其中一个程序员在语法上搞砸了。
你在一座桥上,意识到自己有点朝栏杆走去,于是左转。
1 2 3
| def turn_left
self.turn left:
end |
哎呀!那可能不太好?幸运的是,Ruby培养了一个SyntaxError。
车应该马上停下来-对吗?
不。
1 2 3 4 5 6 7 8 9
| begin
#...
eval self.steering_wheel
#...
rescue Exception => e
self.beep
self.log"Caught #{e}.", :warn
self.log"Logged Error - Continuing Process.", :info
end |
beep beep
Warning: Caught SyntaxError Exception.
Info: Logged Error - Continuing Process.
你发现有问题,就猛击紧急刹车(^C号:Interrupt号)
beep beep
Warning: Caught Interrupt Exception.
Info: Logged Error - Continuing Process.
是的-没什么帮助。你离铁路很近,所以你把车停在了停车场(killing:SignalException)。
beep beep
Warning: Caught SignalException Exception.
Info: Logged Error - Continuing Process.
在最后一秒钟,你拔出钥匙(kill -9),汽车停了下来,你猛地向前撞向方向盘(因为你没有优雅地停止程序,安全气囊不能充气,你终止了程序),汽车后面的电脑猛地撞到前面的座位上。半罐可乐洒在纸上。后面的杂货被压碎了,大部分都被蛋黄和牛奶包裹着。这辆车需要大修和清洗。(数据丢失)
希望你有保险(备份)。哦,是的-因为安全气囊没有充气,你很可能受伤(被解雇等)。
但是等等!您可能想使用rescue Exception => e的原因有很多。
假设你就是那辆车,你想确保如果汽车超过了安全的停止动量,安全气囊就会充气。
1 2 3 4 5 6
| begin
# do driving stuff
rescue Exception => e
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
raise
end |
规则有一个例外:只有你提出例外,你才能抓住Exception。因此,更好的规则是永远不要吞下Exception,并且总是重新提出错误。
但是,在Ruby这样的语言中,添加rescue很容易被忘记,并且在重新提出问题之前添加一个rescue语句感觉有点不干燥。你不想忘记江户十一〔十〕号声明。如果你这样做了,祝你好运,找到那个错误。
幸运的是,Ruby非常棒,您可以只使用ensure关键字,这可以确保代码运行。无论什么情况,ensure关键字都将运行代码-如果抛出异常,如果没有,唯一的异常是世界是否结束(或其他不太可能发生的事件)。
1 2 3 4 5
| begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end |
繁荣!不管怎样,这个代码应该可以运行。您应该使用rescue Exception => e的唯一原因是您需要访问异常,或者您只希望代码在异常上运行。记住要重新提出错误。每一次。
注意:正如@niall指出的,确保始终运行。这是很好的,因为有时您的程序可能会对您撒谎,而不会抛出异常,即使在出现问题时也是如此。对于重要的任务,比如给安全气囊充气,你需要确保不管发生什么事情都会发生。因此,每次停车时检查是否有异常是个好主意。尽管在大多数编程环境中,给气囊充气是一项不常见的任务,但这实际上在大多数清理任务中都很常见。
DR
不要(也不要重新提出例外情况),否则你可能会开下一座桥。
- 哈哈哈!这是一个很好的答案。我很震惊没有人发表评论。你给出了一个清晰的场景,使整个事情真正可以理解。干杯!-)
- @詹姆斯米拉尼谢谢你!
- +??对于这个答案。希望我能不止一次投赞成票!???
- 你的回答很好!
- 这个答案是在完全可以理解和正确接受的答案4年后得出的,并用一个更有趣而非现实的荒谬场景重新解释了它。不好意思,这是一个时髦的说法,但这并不是一个红字——答案简洁、正确比搞笑更重要。另外,关于ensure作为rescue Exception的替代品的部分是误导性的——这个例子意味着它们是等效的,但是正如所述,无论是否有例外,ensure都会发生,所以现在你的气囊会充气,因为你超过了5英里/小时,即使没有出错。
- @尼尔,我更新了我的答案,以改变误导超过5英里每小时检查与self.exceeding_safe_stopping_momentum?。我还添加了一个解释为什么在这种情况下要使用"确保"。rescue Exception并不总是错误的,但ensure通常是一个更好的清理时间,因为它会发生,即使您的程序在无声中失败。
因为这捕获了所有异常。你的程序不太可能从它们中恢复。
您应该只处理知道如何从中恢复的异常。如果您没有预料到某种异常,就不要处理它,大声崩溃(将详细信息写入日志),然后诊断日志并修复代码。
吞咽异常是很糟糕的,不要这样做。
这是一个特殊的规则,你不应该抓住任何你不知道如何处理的例外。如果您不知道如何处理它,最好让系统的其他部分捕获并处理它。