`

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

    博客分类:
  • Ruby
 
阅读更多

异常与返回码相比,能更好的将错误情况从逻辑代码中隔离开来,即可以只关心正常逻辑,或者关心自己可以处理的异常情况。而返回码在一个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

 

 

 

分享到:
评论

相关推荐

    Programming Ruby(读书笔记)-3章

    《Programming Ruby》是一本关于Ruby编程语言的经典书籍,它的第三章深入探讨了Ruby的基本语法和核心概念。在这一章中,作者介绍了变量、常量、符号、数组、哈希等核心数据类型,以及控制流(条件语句和循环)和方法...

    Ruby学习资料(含参考手册和Programming Ruby)-中文.rar

    "ruby--.txt"可能是一个文本文件,其中包含了Ruby的代码示例、笔记或者问题解答,通过阅读可以加深对Ruby语法和实践的理解。 "Ruby语言入门教程附实例"和"ruby-mht"文件很可能是包含实例的教程,实践是学习编程的...

    ruby初学笔记ruby初学笔记

    1. **面向对象编程(Object-Oriented Programming, OOP)**:Ruby是完全的面向对象语言,每个值都是一个对象,包括基本类型如整数、字符串和布尔值。类是创建对象的蓝图,实例化一个类就能创建一个新的对象。理解类...

    Ruby 语言教程从小白到入门

    首先,Ruby的核心概念是面向对象编程(Object-Oriented Programming, OOP)。在Ruby中,一切都是对象,包括基本的数据类型如数字、字符串和布尔值。理解这一点至关重要,因为这意味着你可以对任何东西调用方法,这极...

    programming_language:编程语言学习笔记

    "programming_language:编程语言学习笔记"这一主题旨在整理和探讨各种编程语言的关键特性、语法结构以及在实际开发中的应用。 首先,编程语言可以分为几大类别,如低级语言(机器语言和汇编语言)和高级语言(如C,...

    java俄罗斯方块源码-ossu-cs:通过开源社会大学开设的计算机科学课程

    java俄罗斯框源码开源社会大学 计算机科学课程 罗伯特·福克 核心模块 C100 - 计算机科学课程简介 涵盖的主题: computation imperative programming ...笔记 ...programming ...Ruby 代码 班级 笔记 代码 地

    java版中国象棋源码-learning:学习笔记

    Ruby, Algorithms, and so on in a few days or hours. The Amazon advanced search for [ and found 512 such books. Of the top ten, nine are programming books (the other is about bookkeeping). Similar ...

    rxswift-to-combine-cheatsheet:RxSwift到Apple的合并备忘单

    笔记 任何观察者 任何订阅者 行为继电器 :cross_mark: 围绕BehaviorSubject的简单包装,可以在Combine中轻松地重新创建 行为主体 CurrentValueSubject 这似乎是隐藏@State的类型 可完成的 :cross_mark: 复合一...

    zellytozelly

    在“zellytozelly-main”这个文件名中,如果没有更多的上下文信息,我们可以假设它可能是一个项目的主分支或者主要代码仓库,包含着开发者的学习笔记、代码示例或实际项目的源代码。 总之,“zellytozelly”项目...

    BackendRoadmap

    1. **基础语言和框架**:后端开发通常始于学习一种或多种编程语言,如Python、Java、Ruby或Node.js。每种语言都有其特性和用途,例如Python适合数据分析和快速开发,而Java则因其跨平台能力和企业级应用而流行。 2....

    xjl_fullstack:前端 全栈学习

    "xjl_fullstack:前端 全栈学习" 的标题和描述表明这是一个关于前端和全栈开发的学习资源,可能是某个教程、笔记或者代码仓库。尽管没有提供具体的压缩包内容,我们可以根据标签"HTML"以及文件名"xjl_fullstack-main...

Global site tag (gtag.js) - Google Analytics