对于异常处理,倾向使用 raise
而不是 fail
。
[link]
# 差
fail SomeException, 'message'
# 好
raise SomeException, 'message'
不要在带双参数形式的 raise
方法中显式指定 RuntimeError
。
[link]
# 差
raise RuntimeError, 'message'
# 好 - 默认就是 RuntimeError
raise 'message'
倾向使用带异常类、消息的双参数形式调用 raise
方法,而不是使用异常的实例。
[link]
# 差 - 并无 raise SomeException.new('message') [, backtraces] 这种调用形式
raise SomeException.new('message')
# 好 - 与调用形式 raise SomeException [, 'message' [, backtraces]] 保持一致
raise SomeException, 'message'
永远不要从 ensure
区块返回。如果你显式地从 ensure
区块返回,那么其所在的方法会如同永远不会发生异常般的返回。事实上,异常被默默地丢弃了。
[link]
def foo
raise
ensure
return 'very bad idea'
end
尽可能隐式地使用 begin/rescue/ensure/end
区块。
[link]
# 差
def foo
begin
# 主逻辑
rescue
# 异常处理逻辑
end
end
# 好
def foo
# 主逻辑
rescue
# 异常处理逻辑
end
通过使用 contingency 方法(一个由 Avdi Grimm 创造的词)来减少 begin/rescue/ensure/end
区块的使用。
[link]
# 差
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 }
不要抑制异常。
[link]
# 差
begin
# 抛出异常
rescue SomeError
# 不做任何相关处理
end
# 差
do_something rescue nil
避免使用 rescue
修饰语法。
[link]
# 差 - 这里将会捕捉 StandardError 及其所有子孙类的异常
read_file rescue handle_error($!)
# 好 - 这里只会捕获 Errno::ENOENT 及其所有子孙类的异常
def foo
read_file
rescue Errno::ENOENT => ex
handle_error(ex)
end
不要将异常处理作为流程控制使用。
[link]
# 差
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
来终止进程。
[link]
# 差 - 信号与 exit 方法产生的异常会被捕获(除了 kill -9)
begin
exit
rescue Exception
puts "you didn't really want to exit, right?"
# 处理异常
end
# 好 - 没有指定具体异常的 rescue 子句默认捕获 StandardError
begin
# 抛出异常
rescue => e
# 处理异常
end
# 好 - 指定具体异常 StandardError
begin
# 抛出异常
rescue StandardError => e
# 处理异常
end
把较具体的异常放在处理链的较上层,不然它们永远不会被执行。
[link]
# 差
begin
# 抛出异常
rescue StandardError => e
# 处理异常
rescue IOError => e
# 处理异常,但事实上永远不会被执行
end
# 好
begin
# 抛出异常
rescue IOError => e
# 处理异常
rescue StandardError => e
# 处理异常
end
在 ensure
区块释放程序的外部资源。
[link]
f = File.open('testfile')
begin
# .. 文件操作
rescue
# .. 处理异常
ensure
f.close if f
end
在调用资源获取方法时,尽量使用具备自动清理功能的版本。
[link]
# 差 - 需要显式关闭文件描述符
f = File.open('testfile')
# ...
f.close
# 好 - 文件描述符会被自动关闭
File.open('testfile') do |f|
# ...
end
倾向使用标准库中的异常类而不是引入新的类型。
[link]