* 使用 `fail` 方法来抛出异常。仅在捕捉到异常时使用 `raise` 来重新抛出异常(因为没有失败,所以只是显式地有目的性地抛出一个异常) ~~~ begin fail 'Oops'; rescue => error raise if error.message != 'Oops' end ~~~ * 如果 `fail/raise` 只有两个参数,无需显性指定 `RuntimeError`。 ~~~ # 差 fail RuntimeError, 'message' # 好——默认就是 RuntimeError fail 'message' ~~~ * 将异常类和消息作为参数给 `fail/raise` ,而不是异常类的的实例。 ~~~ # 差 fail SomeException.new('message') # 无法使用 `fail SomeException.new('message'), backtrace`. # 好 fail SomeException, 'message' # 可以使用 `fail SomeException, 'message', backtrace`. ~~~ * 永远不要从 `ensure` 区块返回。如果你显式地从 `ensure` 区块中的一个方法返回,那么这方法会如同没有异常般的返回。实际上,异常会被默默丢掉。 ~~~ def foo begin fail ensure return 'very bad idea' end end ~~~ * 尽可能使用隐式的 `begin` 区块。 ~~~ # 差 def foo begin # 此处放主要逻辑 rescue # 错误处理放在此处 end end # 好 def foo # 此处放主要逻辑 rescue # 错误处理放在此处 end ~~~ * 通过 _contingency_ 方法 (一个由 Avdi Grimm 创造的词) 来减少 `begin` 区块的使用。 ~~~ # 差 begin something_that_might_fail rescue IOError # 处理 IOError end begin something_else_that_might_fail rescue IOError # 处理 IOError end # 好 def with_io_error_handling yield rescue IOError # 处理 IOError end with_io_error_handling { something_that_might_fail } with_io_error_handling { something_else_that_might_fail } ~~~ * 不要抑制异常。 ~~~ begin # 这里发生了一个异常 rescue SomeError # 拯救子句完全没有做事 end # 差 do_something rescue nil ~~~ * 避免使用 `rescue` 的修饰符形式。 ~~~ # 差 - 这捕捉了所有的 StandardError 异常。 do_something rescue nil ~~~ * 不要为了控制流程而使用异常。 ~~~ # 差 begin n / d rescue ZeroDivisionError puts 'Cannot divide by 0!' end # 好 if d.zero? puts 'Cannot divide by 0!' else n / d end ~~~ * 避免救援 `Exception` 类别。这会把信号困住,并呼叫 `exit`,导致你需要 `kill -9` 进程。 ~~~ # 差 begin # 呼叫 exit 及杀掉信号会被捕捉(除了 kill -9) exit rescue Exception puts "you didn't really want to exit, right?" # 异常处理 end # 好 begin # 一个不明确的 rescue 子句捕捉的是 StandardError, # 而不是许多编程者所设想的 Exception。 rescue => e # 异常处理 end # 也好 begin # 这里发生一个异常 rescue StandardError => e # 异常处理 end ~~~ * 把较具体的异常放在救援串连的较上层,不然它们永远不会被拯救。 ~~~ # 差 begin # 一些代码 rescue Exception => e # 一些处理 rescue StandardError => e # 一些处理 end # 好 begin # 一些代码 rescue StandardError => e # 一些处理 rescue Exception => e # 一些处理 end ~~~ * 在 `ensure` 区块中释放你的程式的外部资源。 ~~~ f = File.open('testfile') begin # .. 处理 rescue # .. 错误处理 ensure f.close unless f.nil? end ~~~ * 倾向使用标准库的异常类而不是导入新的异常类。