关于异常:以DRY方式将多个错误类传递给ruby的rescue子句

Passing multiple error classes to ruby's rescue clause in a DRY fashion

我有一些代码需要在ruby中拯救多种类型的异常:

1
2
3
4
5
6
7
8
9
10
begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue FooException, BarException
  puts"rescued!"
end

我想要做的是以某种方式存储我想要在某处救援的异常类型列表,并将这些类型传递给rescue子句:

1
EXCEPTIONS = [FooException, BarException]

然后:

1
rescue EXCEPTIONS

这甚至是可能的,如果没有对eval的一些真正的黑客调用,它是否可能? 考虑到我在尝试上述情况时看到TypeError: class or module required for rescue clause,我没有希望。


您可以使用splat运算符*的数组。

1
2
3
4
5
6
7
8
9
10
11
12
EXCEPTIONS = [FooException, BarException]

begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue *EXCEPTIONS
  puts"rescued!"
end

如果您要使用上面的数组常量(使用EXCEPTIONS),请注意您无法在定义中定义它,如果您在其他类中定义它,则必须使用其命名空间引用它。实际上,它不一定是常数。

Splat操作员

splat运算符*在其位置"解包"一个数组,以便

1
rescue *EXCEPTIONS

意思是一样的

1
rescue FooException, BarException

您也可以在数组文字中使用它

1
[BazException, *EXCEPTIONS, BangExcepion]

这是一样的

1
[BazException, FooException, BarException, BangExcepion]

或者在争论的位置

1
method(BazException, *EXCEPTIONS, BangExcepion)

意思是

1
method(BazException, FooException, BarException, BangExcepion)

[]扩展为空白:

1
[a, *[], b] # => [a, b]

ruby 1.8和ruby 1.9之间的一个区别是nil

1
2
[a, *nil, b] # => [a, b]       (ruby 1.9)
[a, *nil, b] # => [a, nil, b]  (ruby 1.8)

注意定义to_a的对象,因为在这种情况下将应用to_a

1
[a, *{k: :v}, b] # => [a, [:k, :v], b]

对于其他类型的对象,它返回自身。

1
[1, *2, 3] # => [1, 2, 3]


我刚遇到这个问题并找到了另一种解决方案。如果你的FooExceptionBarException都是自定义异常类,特别是如果它们都是主题相关的,你可以构建你的继承层次结构,这样它们都将从同一个父类继承,然后只保护父母班。

例如,我有三个例外:FileNamesMissingErrorInputFileMissingErrorOutputDirectoryError,我想用一个语句进行救援。我创建了另一个名为FileLoadError的异常类,然后设置上述三个异常以继承它。然后我只救了FileLoadError

像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class FileLoadError < StandardError
end

class FileNamesMissingError < FileLoadError
end

class InputFileMissingError < FileLoadError
end

class OutputDirectoryError < FileLoadError
end

[FileNamesMissingError,
 InputFileMissingError,
 OutputDirectoryError].each do |error|
   begin  
     raise error
   rescue FileLoadError => e
     puts"Rescuing #{e.class}."
   end
end