异常
异常
- 对于异常处理,倾向使用
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 }
## 差
begin
# 抛出异常
rescue SomeError
# 不做任何相关处理
end
## 差
do_something rescue nil
## 差 - 这里将会捕捉 StandardError 及其所有子孙类的异常
read_file rescue handle_error($!)
## 好 - 这里只会捕获 Errno::ENOENT 及其所有子孙类的异常
def foo
read_file
rescue Errno::ENOENT => ex
handle_error(ex)
end
## 差
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]