异常

异常

  • 对于异常处理,倾向使用 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]