Ruby异常处理

在现实世界中,所有程序都会出错。一个优秀的程序可以预期错误的发生,并且优雅地处理它们。

一种错误处理的方法是:使用返回码。举个例子,我们在使用open方法打开文件,文件不存在时就会出错。我们可以使用一个特殊的返回码来标识这个错误。

但这种处理方式的问题是:管理这些错误代码会显得非常复杂。比如,我们调用了open,read并最终调用close方法,每一个方法都会返回不同的错误代码,我们需要在调用的外层次使用复杂难懂的代码来管理和区分这些不同的错误代码。

异常机制很好地解决了上述错误处理方法的问题:异常把错误信息打包进一个类中,在抛出一个异常之后,异常会自动在调用栈中‘上浮’,直到遇见声明可以处理相应类型异常的代码。

这些错误代码被解释成了有意义的单词,方便我们记忆。如:NameError

begin    
a    
rescue NameError    
puts "error:#{$!}"    
raise    
end

我们用begin,rescue,end把可能抛出异常的代码以及处理异常的代码包围起来,特别是rescue语句声明了它可以处理的异常类型,rescue之后的代码就是处理异常的代码。

异常对象被抛出之后,存于一个全局变量:$!中。可以看到异常处理代码最后调用了raise,它代表把同一个异常再次抛出。

在一个异常处理块中,可以有多个rescue语句以拥有对不同异常的不同处理,同时一个rescue语句也可以声明捕获多个异常类型。如下面的代码一样:

begin     
eval string     
rescue SyntaxError, NameError => boom     
print "String doesn't compile: " + boom     
rescue StandardError => bang     
print "Error running script: " + bang     
end

只要其中匹配一个rescue的错误,处理异常,然后跳出end

很多时候,无论程序抛异常与否,我们都得保证在最后做一些清理工作。比如一段从文件读取数据的代码,无论读取是否成功,我们都得保证最后关闭这个文件。我们可以通过在任何的退出点关闭文件来解决这个问题,但这样做非常繁琐,也不能保证照顾到了每个退出点。所以,我们需要一种方法来保证。这就是 ensure的作用:

有时我们会希望围绕问题展开创造性工作.这里,如果文件不存在,我们用标准输入代替:

begin
  file = open("some_file")
rescue
  file = STDIN
end
begin
  # ... process the input ...
rescue
  # ... and deal with any other exceptions here.
end

retry 用于 rescue 代码表示又重新执行 begin 代码.这让我们可以压缩前面的例子:

fname = "some_file"
begin
  file = open(fname)
  # ... process the input ...
rescue
  fname = "STDIN"
  retry
end

但这仍有一点瑕疵.一个不存在的文件将导致不停止地 retry.你在使用 retry 做异常处理时应注意到这一点.

每个Ruby库在遇到错误时都会提交一个异常,你可以在自己的代码里明确地提交异常.用 raise 来提交异常.它带一个参数,也就是描述异常的一个字符串.参数是可选的但不应被省略.之后它可以通过一个特殊的全局变量 $! 访问.

其实 begin相当于c#的try, rescue相当于c#的catch, ensure相当于c#的 finaly, raise相当于c#的 throw 。

3 weeks ago, this page was being read.

,

Subscribe to Comments