Rubyの例外


例外と実行は常にリンクされています。存在しないファイルを開いて状況を適切に処理しなかった場合、プログラムの品質は低いと見なされます。

例外が発生するとプログラムは停止します。例外は、プログラムの実行中に発生する可能性のあるさまざまなタイプのエラーを処理するために使用されるため、プログラムを完全に停止させることなく適切なアクションが実行されます。

Ruby は、例外を処理するための完璧なメカニズムを提供します。例外をスローする可能性のあるコードを begin/end ブロックで囲み、rescue 節を使用して Ruby にどのタイプの例外を処理するかを指示できます。

文法

begin #开始
 
 raise.. #抛出异常
 
rescue [ExceptionType = StandardException] #捕获指定类型的异常 缺省值是StandardException
 $! #表示异常信息
 $@ #表示异常出现的代码位置
else #其余异常
 ..
ensure #不管有没有异常,进入该代码块
 
end #结束

beginからrescueまですべてが保護されています。コード ブロックの実行中に例外が発生した場合、制御は rescueend の間のブロックに渡されます。

beginブロック内のrescue句ごとに、Rubyはスローされた例外を各引数と順番に比較します。レスキュー句で指定された例外が、現在スローされている例外と同じ型であるか、その例外の親クラスである場合、一致は成功します。

例外が指定されたすべてのエラー タイプに一致しない場合は、すべての rescue 句の後に else 句を使用できます。

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

上記の例の出力結果は以下の通りです。 openが失敗したため、STDINfileを置き換えていることがわかります。

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

retryステートメントの使用

rescueブロックを使用して例外をキャッチし、次にretryステートメントを使用してbeginブロックの先頭から実行を開始できます。

構文

begin
    # 这段代码抛出的异常将被下面的 rescue 子句捕获
rescue
    # 这个块将捕获所有类型的异常
    retry  # 这将把控制移到 begin 的开头
end

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

以下が処理の流れです:

  • オープン中に例外が発生しました。

  • 救助にスキップしてください。 fname が再割り当てされます。

  • 再試行により、begin の先頭にジャンプします。

  • 今度はファイルが正常に開きました。

  • 基本的なプロセスを続けます。

注: 名前を変更したファイルが存在しない場合、この強制コードは無限に試行されます。したがって、例外を処理するときは、注意して retry を使用してください。

raise ステートメントを使用する

raise ステートメントを使用して例外をスローできます。次のメソッドは呼び出されると例外をスローします。 2 番目のメッセージが出力されます。

構文

raise 

或

raise "Error Message" 

或

raise ExceptionType, "Error Message"

或

raise ExceptionType, "Error Message" condition

最初の形式は、現在の例外 (または、現在の例外がない場合は RuntimeError) を単純に再スローします。これは、例外を渡す前に解釈する必要がある例外ハンドラーで使用されます。

2 番目の形式は、新しい RuntimeError 例外を作成し、そのメッセージを指定された文字列に設定します。その後、例外が呼び出しスタックにスローされます。

3 番目の形式は、最初のパラメーターを使用して例外を作成し、関連するメッセージを 2 番目のパラメーターとして設定します。

4 番目の形式は 3 番目の形式に似ており、追加の条件ステートメント (unless など) を追加して例外をスローできます。

#!/usr/bin/ruby

begin  
    puts 'I am before the raise.'  
    raise 'An error has occurred.'  
    puts 'I am after the raise.'  
rescue  
    puts 'I am rescued.'  
end  
puts 'I am after the begin block.'

上記の例の出力結果は次のとおりです:

I am before the raise.  
I am rescued.  
I am after the begin block.

raiseの使用法を示す別の例:

#!/usr/bin/ruby

begin  
  raise 'A test exception.'  
rescue Exception => e  
  puts e.message  
  puts e.backtrace.inspect  
end

上記の例の出力結果は次のとおりです:

A test exception.
["main.rb:4"]

ensureステートメントを使用します

場合によっては、例外がスローされるかどうかに関係なく、コード ブロックの最後で何らかの処理が完了していることを確認する必要があります。たとえば、ブロックに入るときにファイルが開いている場合、ブロックから出るときは必ずファイルを閉じる必要があります。

これが

ensure 句の動作です。 ensure は最後のレスキュー句の後に配置され、ブロックの終了時に常に実行されるコード ブロックが含まれます。ブロックが正常に終了するか、例外がスローされて処理されるか、キャッチされない例外で終了するかに関係なく、ensure ブロックは常に実行されます。

文法

begin 
   #.. 过程
   #.. 抛出异常
rescue 
   #.. 处理错误 
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
end

begin
  raise 'A test exception.'
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
ensure
  puts "Ensuring execution"
end

上記の例の出力結果は次のとおりです:

A test exception.
["main.rb:4"]
Ensuring execution

elseステートメントを使用します

else句が提供される場合、通常はrescue句の後に配置されます。事前に必ず確認してください。

else 句の本体は、コードの本体が例外をスローしない場合にのみ実行されます。

構文

begin 
   #.. 过程 
   #.. 抛出异常
rescue 
   #.. 处理错误
else
   #.. 如果没有异常则执行
ensure 
   #.. 最后确保执行
   #.. 这总是会执行
end

begin
 # 抛出 'A test exception.'
 puts "I'm not raising exception"
rescue Exception => e
  puts e.message
  puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
  puts "Ensuring execution"
end

上記の例の出力結果は次のとおりです:

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

$! 変数を使用して、スローされたエラー メッセージをキャプチャします。

Catch と Throw

raise と Rescue の例外メカニズムは、エラーが発生したときに、通常の処理中に深くネストされた構造からジャンプする必要がある場合があります。ここでキャッチアンドスローが役に立ちます。

catch は、指定された名前 (シンボルまたは文字列) をラベルとして使用するブロックを定義します。ブロックはスローが発生するまで通常どおり実行されます。

構文

throw :lablename
#.. 这不会被执行
catch :lablename do
#.. 在遇到一个 throw 后匹配将被执行的 catch
end

或

throw :lablename condition
#.. 这不会被执行
catch :lablename do
#.. 在遇到一个 throw 后匹配将被执行的 catch
end

次の例では、ユーザーがプロンプトに応じて「!」と入力すると、ユーザーとの対話を終了するためにスローが使用されます。

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: ")
   # ..
   # 处理信息
end
promptAndGet("Name:")

上記のプログラムは人間の介入を必要としますが、コンピュータ上で試すことができます。上記の例の出力結果は次のようになります:

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Class Exception

Ruby の標準クラスとモジュールは例外をスローします。最上位の Exception クラスを含め、すべての例外クラスは階層を形成します。次のレベルは 7 つの異なるタイプです:

  • Interrupt

  • NoMemoryError

  • SignalException

  • ScriptError

  • StandardError

  • SystemExit

Fatal はこの中の別の関数ですレイヤー 例外ですが、Ruby インタプリタは内部でのみ使用します。

ScriptError と StandardError の両方にいくつかのサブクラスがありますが、ここではそれらの詳細を知る必要はありません。最も重要なことは、独自の例外クラスを作成することです。例外クラスは、クラス Exception またはその子孫のサブクラスである必要があります。

例を見てみましょう:

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

次に、上記の例外が使用される次の例を見てください:

File.open(path, "w") do |file|
begin
    # 写出数据 ...
rescue
    # 发生错误
    raise FileSaveError.new($!)
end
end

ここで、最も重要な行は raise

FileSaveError.new($!) です。 raise を呼び出して例外が発生したことを示し、特定の例外によりデータの書き込みに失敗した FileSaveError の新しいインスタンスに例外を渡します。