国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

directory search
Ruby用戶指南 3、開始 4、簡單的例子 5、字符串 6、正則表達式 7、數(shù)組 8、回到那些簡單的例子 9、流程控制 10、迭代器 11、面向對象思維 12、方法 13、類 14、繼承 15、重載方法 16、訪問控制 17、單態(tài)方法 18、模塊 19、過程對象 20、變量 21、全局變量 22、實變量 23、局部變量 24、類常量 25、異常處理:rescue 26、異常處理:ensure 27、存取器 28、對象的初始化 29、雜項 RGSS入門教程 1、什么是RGSS 2、開始:最簡單的腳本 3、數(shù)據(jù)類型:數(shù)字 4、數(shù)據(jù)類型:常量與變量 5、數(shù)據(jù)類型:字符串 6、控制語句:條件分歧語句 7、控制語句:循環(huán) 8、函數(shù) 9、對象與類 10、顯示圖片 11、數(shù)組 12、哈希表(關聯(lián)數(shù)組) 13、類 14、數(shù)據(jù)庫 15、游戲對象 16、精靈的管理 17、窗口的管理 18、活動指令 19、場景類 Programming Ruby的翻譯 Programming Ruby: The Pragmatic Programmer's Guide 前言 Roadmap Ruby.new 類,對象和變量 容器Containers,塊Blocks和迭代Iterators 標準類型 深入方法 表達式Expressions 異常,捕捉和拋出(已經(jīng)開始,by jellen) 模塊 基本輸入輸出 線程和進程 當遭遇挫折 Ruby和它的世界 Ruby和Web開發(fā) Ruby Tk Ruby 和微軟的 Windows 擴展Ruby Ruby語言 (by jellen) 類和對象 (by jellen) Ruby安全 反射Reflection 內建類和方法 標準庫 OO設計 網(wǎng)絡和Web庫 Windows支持 內嵌文檔 交互式Ruby Shell 支持 Ruby參考手冊 Ruby首頁 卷首語 Ruby的啟動 環(huán)境變量 對象 執(zhí)行 結束時的相關處理 線程 安全模型 正則表達式 字句構造 程序 變量和常數(shù) 字面值 操作符表達式 控制結構 方法調用 類/方法的定義 內部函數(shù) 內部變量 內部常數(shù) 內部類/模塊/異常類 附加庫 Ruby變更記錄 ruby 1.6 特性 ruby 1.7 特性 Ruby術語集 Ruby的運行平臺 pack模板字符串 sprintf格式 Marshal格式 Ruby FAQ Ruby的陷阱
characters

Exceptions, Catch, and Throw



So far we're been developing code in Pleasantville, a wonderful place where nothing ever, ever goes wrong. Every library call succeeds, users never enter incorrect data, and resources are plentiful and cheap. Well, that's about to change. Welcome to the real world!

In the real world, errors happen. Good programs (and programmers) anticipate them and arrange to handle them gracefully. This isn't always as easy as it might be. Often the code that detects an error does not have the context to know what to do about it. For example, attempting to open a file that doesn't exist is acceptable in some circumstances and is a fatal error at other times. What's your file-handling module to do?

The traditional approach is to use return codes. The open method returns some specific value to say it failed. This value is then propagated back through the layers of calling routines until someone wants to take responsibility for it.

The problem with this approach is that managing all these error codes can be a pain. If a function calls open, then read, and finally close, and each can return an error indication, how can the function distinguish these error codes in the value it returns to its caller?

To a large extent, exceptions solve this problem. Exceptions let you package up information about an error into an object. That exception object is then propagated back up the calling stack automatically until the runtime system finds code that explicitly declares that it knows how to handle that type of exception.

The Exception Class

The package that contains the information about an exception is an object of class Exception, or one of class Exception's children. Ruby predefines a tidy hierarchy of exceptions, shown in Figure 8.1 on page 91. As we'll see later, this hierarchy makes handling exceptions considerably easier.

Figure not available...

When you need to raise an exception, you can use one of the built-in Exception classes, or you can create one of your own. If you create your own, you might want to make it a subclass of StandardError or one of its children. If you don't, your exception won't be caught by default.

Every Exception has associated with it a message string and a stack backtrace. If you define your own exceptions, you can add additional information.

Handling Exceptions

Our jukebox downloads songs from the Internet using a TCP socket. The basic code is simple:

opFile?=?File.open(opName,?"w")
while?data?=?socket.read(512)
??opFile.write(data)
end

What happens if we get a fatal error halfway through the download? We certainly don't want to store an incomplete song in the song list. ``I Did It My *click*''.

Let's add some exception handling code and see how it helps. We enclose the code that could raise an exception in a begin/end block and use rescue clauses to tell Ruby the types of exceptions we want to handle. In this case we're interested in trapping SystemCallError exceptions (and, by implication, any exceptions that are subclasses of SystemCallError), so that's what appears on the rescue line. In the error handling block, we report the error, close and delete the output file, and then reraise the exception.

opFile?=?File.open(opName,?"w")
begin
??#?Exceptions?raised?by?this?code?will
??#?be?caught?by?the?following?rescue?clause
??while?data?=?socket.read(512)
????opFile.write(data)
??end

rescue?SystemCallError ??$stderr.print?"IO?failed:?"?+?$! ??opFile.close ??File.delete(opName) ??raise end

When an exception is raised, and independent of any subsequent exception handling, Ruby places a reference to the Exception object associated with the exception in the global variable $! (the exclamation point presumably mirroring our surprise that any of our code could cause errors). In the previous example, we used this variable to format our error message.

After closing and deleting the file, we call raise with no parameters, which reraises the exception in $!. This is a useful technique, as it allows you to write code that filters exceptions, passing on those you can't handle to higher levels. It's almost like implementing an inheritance hierarchy for error processing.

You can have multiple rescue clauses in a begin block, and each rescue clause can specify multiple exceptions to catch. At the end of each rescue clause you can give Ruby the name of a local variable to receive the matched exception. Many people find this more readable than using $! all over the place.

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

How does Ruby decide which rescue clause to execute? It turns out that the processing is pretty similar to that used by the case statement. For each rescue clause in the begin block, Ruby compares the raised exception against each of the parameters in turn. If the raised exception matches a parameter, Ruby executes the body of the rescue and stops looking. The match is made using $!.kind_of?(parameter), and so will succeed if the parameter has the same class as the exception or is an ancestor of the exception. If you write a rescue clause with no parameter list, the parameter defaults to StandardError.

If no rescue clause matches, or if an exception is raised outside a begin/end block, Ruby moves up the stack and looks for an exception handler in the caller, then in the caller's caller, and so on.

Although the parameters to the rescue clause are typically the names of Exception classes, they can actually be arbitrary expressions (including method calls) that return an Exception class.

Tidying Up

Sometimes you need to guarantee that some processing is done at the end of a block of code, regardless of whether an exception was raised. For example, you may have a file open on entry to the block, and you need to make sure it gets closed as the block exits.

The ensure clause does just this. ensure goes after the last rescue clause and contains a chunk of code that will always be executed as the block terminates. It doesn't matter if the block exits normally, if it raises and rescues an exception, or if it is terminated by an uncaught exception---the ensure block will get run.

f?=?File.open("testfile")
begin
??#?..?process
rescue
??#?..?handle?error
ensure
??f.close?unless?f.nil?
end

The else clause is a similar, although less useful, construct. If present, it goes after the rescue clauses and before any ensure. The body of an else clause is executed only if no exceptions are raised by the main body of code.

f?=?File.open("testfile")
begin
??#?..?process
rescue
??#?..?handle?error
else
??puts?"Congratulations--?no?errors!"
ensure
??f.close?unless?f.nil?
end

Play It Again

Sometimes you may be able to correct the cause of an exception. In those cases, you can use the retry statement within a rescue clause to repeat the entire begin/end block. Clearly there is tremendous scope for infinite loops here, so this is a feature to use with caution (and with a finger resting lightly on the interrupt key).

As an example of code that retries on exceptions, have a look at the following, adapted from Minero Aoki's net/smtp.rb library.

@esmtp?=?true

begin ??#?First?try?an?extended?login.?If?it?fails?because?the ??#?server?doesn't?support?it,?fall?back?to?a?normal?login

??if?@esmtp?then ????@command.ehlo(helodom) ??else ????@command.helo(helodom) ??end

rescue?ProtocolError ??if?@esmtp?then ????@esmtp?=?false ????retry ??else ????raise ??end end

This code tries first to connect to an SMTP server using the EHLO command, which is not universally supported. If the connection attempt fails, the code sets the @esmtp variable to false and retries the connection. If this fails again, the exception is reraised up to the caller.

Raising Exceptions

So far we've been on the defensive, handling exceptions raised by others. It's time to turn the tables and go on the offensive. (There are those that say your gentle authors are always offensive, but that's a different book.)

You can raise exceptions in your code with the Kernel::raise method.

raise
raise?"bad?mp3?encoding"
raise?InterfaceException,?"Keyboard?failure",?caller

The first form simply reraises the current exception (or a RuntimeError if there is no current exception). This is used in exception handlers that need to intercept an exception before passing it on.

The second form creates a new RuntimeError exception, setting its message to the given string. This exception is then raised up the call stack.

The third form uses the first argument to create an exception and then sets the associated message to the second argument and the stack trace to the third argument. Typically the first argument will be either the name of a class in the Exception hierarchy or a reference to an object instance of one of these classes.[Technically, this argument can be any object that responds to the message exception by returning an object such that object.kind_of?(Exception) is true.] The stack trace is normally produced using the Kernel::caller method.

Here are some typical examples of raise in action.

raise

raise?"Missing?name"?if?name.nil?

if?i?>=?myNames.size ??raise?IndexError,?"#{i}?>=?size?(#{myNames.size})" end

raise?ArgumentError,?"Name?too?big",?caller

In the last example, we remove the current routine from the stack backtrace, which is often useful in library modules. We can take this further: the following code removes two routines from the backtrace.

raise?ArgumentError,?"Name?too?big",?caller[1..-1]

Adding Information to Exceptions

You can define your own exceptions to hold any information that you need to pass out from the site of an error. For example, certain types of network errors might be transient depending on the circumstances. If such an error occurs, and the circumstances are right, you could set a flag in the exception to tell the handler that it might be worth retrying the operation.

class?RetryException?<?RuntimeError
??attr?:okToRetry
??def?initialize(okToRetry)
????@okToRetry?=?okToRetry
??end
end

Somewhere down in the depths of the code, a transient error occurs.

def?readData(socket)
??data?=?socket.read(512)
??if?data.nil?
????raise?RetryException.new(true),?"transient?read?error"
??end
??#?..?normal?processing
end

Higher up the call stack, we handle the exception.

begin
??stuff?=?readData(socket)
??#?..?process?stuff
rescue?RetryException?=>?detail
??retry?if?detail.okToRetry
??raise
end

Catch and Throw

While the exception mechanism of raise and rescue is great for abandoning execution when things go wrong, it's sometimes nice to be able to jump out of some deeply nested construct during normal processing. This is where catch and throw come in handy.

catch?(:done)??do
??while?gets
????throw?:done?unless?fields?=?split(/\t/)
????songList.add(Song.new(*fields))
??end
??songList.play
end

catch defines a block that is labeled with the given name (which may be a Symbol or a String). The block is executed normally until a throw is encountered.

When Ruby encounters a throw, it zips back up the call stack looking for a catch block with a matching symbol. When it finds it, Ruby unwinds the stack to that point and terminates the block. If the throw is called with the optional second parameter, that value is returned as the value of the catch. So, in the previous example, if the input does not contain correctly formatted lines, the throw will skip to the end of the corresponding catch, not only terminating the while loop but also skipping the playing of the song list.

The following example uses a throw to terminate interaction with the user if ``!'' is typed in response to any prompt.

def?promptAndGet(prompt)
??print?prompt
??res?=?readline.chomp
??throw?:quitRequested?if?res?==?"!"
??return?res
end

catch?:quitRequested?do ??name?=?promptAndGet("Name:?") ??age??=?promptAndGet("Age:??") ??sex??=?promptAndGet("Sex:??") ??#?.. ??#?process?information end

As this example illustrates, the throw does not have to appear within the static scope of the catch.


Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide"
Copyright
Previous article: Next article: