Programming Ruby(读书笔记)-10章(异常)

异常与返回码相比,能更好的将错误情况从逻辑代码中隔离开来,即可以只关心正常逻辑,或者关心自己可以处理的异常情况。而返回码在一个call后面有多个子操作时,如果出现异常情况,则调用者较难知道是哪一步出错。

10.1 异常类

所有内置异常类都继承处Exception类。

10.2处理异常

#使用begin...rescue...end来处理抛出的异常
require 'open-uri'
page= "podcasts"
file_name = "#{page}.html"
web_page = open("http://pragprog.com/#{page}")
output = File.open(file_name,"w")
begin
  while line = web_page.gets
    output.puts line
  end
  output.close
rescue Exception
  STDERR.puts "Failed to download#{page}:#{$!}"
  output.close
  File.delete(file_name)
  raise
end
# $!是全局变量(Global variable)中保存的当前的异常对象

 这种机制与java的相同

#与java的catch相同,可以有多个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时的匹配规则类似于case语句。实际就执行parameter===$!,如果rescue指定的异常类与$!对象的类是同一个或者是其子类,就会执行rescue中的代码。

rescue后面也可以不指定捕获的异常,这时默认使用StandardError。

rescue后面可以是表达式,只要返回的是Exception类的对象。

 

系统错误

调用操作系统操作时,出错返回错误码,ruby会包装它们为SystemCallError的子类,在模块Errno中。

 

异常后的资源清理(Finally)

使用 ensure 来保证在异常后执行批定的代码。ensure放在最后一个rescue的后面。

f = File.open("testFile")
begin
  #
rescue
  #
ensure
  f.close
end
#open方法不需要放在begin块中,因为如果打开抛出异常,是不需要关闭的

 rescue还可以使用else来处理当异常没有发生的时候需要执行的代码

f=File.open("testfile")
begin
  #..process
rescue
  #..handleerror
else
  puts"Congratulations--noerrors!"
ensure
  f.close
end

 

出错后重试

当出错后,希望再次执行(修改某些参数后),可使用retry块。

retry块是放在rescue块中使用。

#下面演示在出错后,修改@estmp的值,然后重试
@esmtp = true
begin
  if @estmp then @command.ehlo(helodom)
                   else @command.helo(helodom)
  end

rescue ProtocolError
  if @estmp then
    @estmp = false
    retry
  else
    raise
  end
end

 

10.3 Raising Exceptions(与throw不同)

可主动raise一个异常。raise与fail都是Object类的方法(区别?)。

构造方法有三个

raise
raise "bad mp3 encoding"
raise InterfaceException, "keyboard failure", caller

第一种:抛出当前异常$!(在rescue块中时),或者是一个RuntimeError($!不存在时);

第二种:创建新的RuntimeError异常,并设置描述

第三种:创建新的异常对象,并把第二个参数的内容赋给它,然后把当前运行堆栈设给第三个参数。Object#caller方法返回的就是当前运行堆栈。

#删除两个栈路由,然后返回
raise ArgumentError, "Name too big", caller[1..-1]

 

自定义Exception

#下面演示定义一个异常类。
class RetryException < RuntimeError
  attr :ok_to_retry
  def initialize(ok_to_retry)
    @ok_to_retry = ok_to_retry
  end
end
#抛出自定义的异常
def read_data(socket)
  data = socket.read(512)
  if data.nil?
    raise RetryException.new(true), "transient read error"
  end
  #..normal processing
end
#rescue中判断
begin
  stuff = read_data(socket)
  #..process stuff
rescue RetryException => detail
  retry if detail.ok_to_retry
  raise
end

 

10.4 catch and throw

 类似于goto语句?在某中正常情况下,希望跳出执行体,放弃已经执行的这一部分内容。这时可使用catch与throw。

word_list = File.open("wordlist")
catch (:done) do
  result = []
  while line = word_list.gets
    word = line.chomp
    throw :done unless word =~ /^\w+$/
    result << word
  end
  puts result.reverse
end

 catch定义一个代码块,通过指定一个标签名(常是symbol或string)。如果throw不发生,就正常执行整个块代码。

当发生throw时,ruby压缩堆栈到匹配的catch点,然后终止当前执行块。throw有一个可选参数,如果赋值后,可以将catch块的返回赋值给一个变量。

word_list=File.open("wordlist")
word_in_error=catch(:done) do
  result=[]
  while line=word_list.gets
    word=line.chomp
    throw(:done,word) unless word=~/^\w+$/
    result<<word
  end
  puts result.reverse
end
if word_in_error
  puts"Failed:'#{word_in_error}'found,but a word was expected"
end
produces:
Failed:'*wow*'found,butawordwasexpected

 throw并不一定静态的与catch存在于一个块内,下面定义一个方法会throw,然后调用时使用catch。

def prompt_and_get(prompt)
  print prompt
  res = readline.chomp
  throw :quit_requested if res=="!"
  res
end
catch:quit_requested do
  name =prompt_and_get("Name:")
  age =prompt_and_get("Age: ")
  sex =prompt_and_get("Sex: ")
  #..
  #processinformation
end

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值