Ruby 为什么“拯救例外=>”是一种糟糕的风格;用红宝石做的?
Ryan Davis说(没有解释): 不要拯救例外。曾经否则我会刺你Ruby 为什么“拯救例外=>”是一种糟糕的风格;用红宝石做的?,ruby,exception-handling,Ruby,Exception Handling,Ryan Davis说(没有解释): 不要拯救例外。曾经否则我会刺你 为什么不呢?正确的做法是什么?因为这样可以捕获所有异常。您的程序不太可能从任何中恢复 您应该只处理您知道如何从中恢复的异常。如果您没有预料到某种异常,请不要处理它,大声崩溃(将详细信息写入日志),然后诊断日志并修复代码 吞咽异常不好,不要这样做。TL;DR:使用标准错误代替常规异常捕获。重新引发原始异常时(例如,当救援以仅记录异常时),救援异常可能没问题 Exception是的根,因此当您rescue Exception时,
为什么不呢?正确的做法是什么?因为这样可以捕获所有异常。您的程序不太可能从任何中恢复 您应该只处理您知道如何从中恢复的异常。如果您没有预料到某种异常,请不要处理它,大声崩溃(将详细信息写入日志),然后诊断日志并修复代码
吞咽异常不好,不要这样做。TL;DR:使用
标准错误
代替常规异常捕获。重新引发原始异常时(例如,当救援以仅记录异常时),救援异常
可能没问题
Exception
是的根,因此当您rescue Exception
时,您可以从所有内容中进行解救,包括SyntaxError
、LoadError
和Interrupt
等子类
救援中断
可防止用户使用CTRLC退出程序
救援SignalException
会阻止程序正确响应信号。除非通过kill-9
,否则它是不可修改的
拯救SyntaxError
意味着失败的eval
s将以静默方式进行
所有这些都可以通过运行此程序并尝试CTRLC或kill
it来显示:
loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end
从异常中营救甚至不是默认设置。做
begin
# iceberg!
rescue
# lifeboats
end
不从异常中救援
,它从标准错误中救援。通常,您应该指定比默认的StandardError
更具体的内容,但是从异常中营救会扩大范围,而不是缩小范围,并且可能会产生灾难性的结果,使错误查找变得极其困难
如果您确实希望从StandardError
中解救,并且您需要一个例外变量,则可以使用以下表格:
begin
# iceberg!
rescue => e
# lifeboats
end
这相当于:
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
从异常中营救正常的少数常见情况之一是出于日志记录/报告目的,在这种情况下,您应该立即重新引发异常:
begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
这是规则的一个特殊情况,您不应该捕获任何您不知道如何处理的异常。如果您不知道如何处理它,最好让系统的其他部分捕获并处理它。真正的规则是:不要丢弃异常。你引用的作者的客观性是值得怀疑的,因为它以
否则我会刺你
当然,要注意信号(默认情况下)抛出异常,通常长时间运行的进程通过信号终止,因此捕获异常和不终止信号异常将使程序很难停止。所以不要这样做:
#! /usr/bin/ruby
while true do
begin
line = STDIN.gets
# heavy processing
rescue Exception => e
puts "caught exception #{e}! ohnoes!"
end
end
不,真的,不要这样做。甚至不要运行它来检查它是否有效
但是,假设您有一个线程服务器,并且希望所有异常都不会:
被忽略(默认值)
停止服务器(如果您说thread.abort\u on\u exception=true,则会发生这种情况)李>
那么这在您的连接处理线程中是完全可以接受的:
begin
# do stuff
rescue Exception => e
myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
myLogger.error("Stack trace: #{backtrace.map {|l| " #{l}\n"}.join}")
end
以上是Ruby默认异常处理程序的一个变体,其优点是它不会杀死您的程序。Rails在其请求处理程序中实现了这一点
在主线程中引发信号异常。后台线程无法获取它们,因此在那里尝试捕获它们是没有意义的
这在生产环境中尤其有用,因为在生产环境中,您不希望程序在出现问题时简单地停止。然后,您可以将堆栈转储放在日志中,并添加到代码中,以更优雅的方式进一步处理调用链中的特定异常
还请注意,还有另一个Ruby习惯用法具有大致相同的效果:
a = do_something rescue "something else"
在这一行中,如果dou\u something
引发异常,它将被Ruby捕获并丢弃,并且a
被指定为“其他东西”
一般来说,不要这样做,除非在特殊情况下,你知道你不需要担心。一个例子:
debugger rescue nil
debugger
函数是在代码中设置断点的一种非常好的方法,但是如果在调试器和Rails之外运行,则会引发异常。从理论上讲,您不应该将调试代码留在程序中(pff!没有人这样做!),但出于某种原因,您可能希望将其保留一段时间,但不要继续运行调试程序
注:
如果您运行了其他人的程序,该程序捕获信号异常并忽略它们(如上面的代码所示),则:
- 在Linux中,在shell中键入
pgrep ruby
,或ps | grep ruby
,查找有问题的程序的PID,然后运行kill-9
李>
- 在Windows中,使用任务管理器(CTRL-SHIFT-ESC),转到“进程”选项卡,找到您的进程,右键单击它并选择“结束进程”
如果您使用的是其他人的程序,无论出于何种原因,该程序充斥着这些忽略异常块,那么将其放在主线的顶部是一种可能的逃避:
%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
这使得程序通过立即终止来响应正常终止信号,绕过异常处理程序,而不进行清理。因此,它可能会导致数据丢失或类似情况。小心
如果您需要这样做:
begin
do_something
rescue Exception => e
critical_cleanup
raise
end
实际上,您可以这样做:
begin
do_something
ensure
critical_cleanup
end
在第二种情况下,无论是否引发异常,每次都将调用
关键清理
TL;博士
不要rescue Exception=>e
(不要重新引发异常)-否则您可能会开车离开驾驶台
假设你是
def turn_left
self.turn left:
end
begin
#...
eval self.steering_wheel
#...
rescue Exception => e
self.beep
self.log "Caught #{e}.", :warn
self.log "Logged Error - Continuing Process.", :info
end
begin
# do driving stuff
rescue Exception => e
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
raise
end
begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end