web-dev-qa-db-ja.com

Rubyで始めましょう

私は最近Rubyでプログラミングを始めました、そして私は例外処理を見ています。

私はensureがC#のfinallyと同等のRubyなのか疑問に思いましたか私は持っているべきです:

file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
rescue
  #handle the error here
ensure
  file.close unless file.nil?
end

または私はこれをするべきですか?

#store the file
file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
  file.close
rescue
  #handle the error here
ensure
  file.close unless file.nil?
end

たとえ例外が発生しなくても、ensureは何に関係なく呼び出されますか?

503
ThePower

はい、ensureはコードが常に評価されることを保証します。それがensureと呼ばれる理由です。したがって、これはJavaおよびC#のfinallyと同等です。

begin/rescue/else/ensure/endの一般的な流れは次のようになります。

begin
  # something which might raise an exception
rescue SomeExceptionClass => some_variable
  # code that deals with some exception
rescue SomeOtherException => some_other_variable
  # code that deals with some other exception
else
  # code that runs only if *no* exception was raised
ensure
  # ensure that this code always runs, no matter what
  # does not change the final value of the block
end

rescueensureelseは除外できます。変数を省略することもできます。その場合は、例外処理コードで例外を調べることはできません。例外クラスを除外することもできます。その場合は、StandardErrorから継承したすべての例外がキャッチされます。 (ExceptionのインスタンスであるがStandardErrorのインスタンスではない例外があるため、これが all / exceptionがキャッチされることを意味するわけではないことに注意してください。SystemStackErrorNoMemoryErrorSecurityErrorNotImplementedErrorLoadErrorSyntaxErrorScriptErrorInterruptSignalException、またはSystemExit

いくつかのブロックは暗黙の例外ブロックを形成します。例えば、メソッド定義は暗黙的に例外ブロックでもあるので、書くのではなく

def foo
  begin
    # ...
  rescue
    # ...
  end
end

あなただけ書く

def foo
  # ...
rescue
  # ...
end

または

def foo
  # ...
ensure
  # ...
end

同じことがclass定義およびmodule定義にも当てはまります。

しかし、あなたが質問している特定のケースでは、実際にははるかに優れたイディオムがあります。一般に、最後にクリーンアップする必要があるリソースを扱うときは、すべてのクリーンアップを行うメソッドにブロックを渡すことによってそれを行います。これはC#のusingブロックに似ていますが、Rubyは実際には非常に強力なので、Microsoftの大祭司が山から降りてきてコンパイラを優雅に変更するのを待つ必要はありません。 Rubyでは、それを自分で実装することができます。

# This is what you want to do:
File.open('myFile.txt', 'w') do |file|
  file.puts content
end

# And this is how you might implement it:
def File.open(filename, mode='r', perm=nil, opt=nil)
  yield filehandle = new(filename, mode, perm, opt)
ensure
  filehandle&.close
end

そして、あなたは何を知っていますか:これは すでに File.openとしてコアライブラリで利用可能です。しかし、あらゆる種類のリソースのクリーンアップ(C#ではusing)やトランザクション、その他考えられるものを実装するために、あなた自身のコードでも使用できる一般的なパターンです。

リソースの取得と解放がプログラムのさまざまな部分に分散されている場合、これが機能しない唯一のケースです。しかし、あなたの例のようにローカライズされていれば、これらのリソースブロックを簡単に使うことができます。


ところで:現代のC#では、usingは実際には不要です。Rubyスタイルのリソースブロックを自分で実装できるからです。

class File
{
    static T open<T>(string filename, string mode, Func<File, T> block)
    {
        var handle = new File(filename, mode);
        try
        {
            return block(handle);
        }
        finally
        {
            handle.Dispose();
        }
    }
}

// Usage:

File.open("myFile.txt", "w", (file) =>
{
    file.WriteLine(contents);
});
1106
Jörg W Mittag

参考までに、rescueセクションで例外が発生した場合でも、コードの実行が次の例外ハンドラに進む前にensureブロックが実行されます。例えば:

begin
  raise "Error!!"
rescue
  puts "test1"
  raise # Reraise exception
ensure
  puts "Ensure block"
end
33
alup

ファイルを確実に閉じるには、File.openのブロック形式を使用します。

File.open("myFile.txt", "w") do |file|
  begin
    file << "#{content} \n"
  rescue
  #handle the error here
  end
end
13
Farrel

はい、ensureはどんな状況でも呼ばれます。詳細については、Programming Rubyの本の「 例外、キャッチ、およびスロー 」を参照して、「sure」を検索してください。

6
Milan Novota

はい、ensureは毎回実行されるので、beginブロックにfile.closeは必要ありません。

ところで、テストするための良い方法は、することです:

begin
  # Raise an error here
  raise "Error!!"
rescue
  #handle the error here
ensure
  p "=========inside ensure block"
end

例外があるときに "========= inside sure block"が表示されるかどうかをテストすることができます。それから、エラーが発生したステートメントをコメントアウトして、ensureステートメントが実行されたかどうかを確認することで、何かが出力されるかどうかを確認できます。

4
Aaron Qian

はい、ensureのようなfinally ブロックが実行されることを保証します。これは、重要なリソースが確実に保護されるようにするために非常に便利です。エラー時にファイルハンドルを閉じるか、ミューテックスを解放します。

3
Chris McCauley

これがensureが必要な理由です。

def hoge
  begin
    raise
  rescue  
    raise # raise again
  ensure  
    puts 'ensure' # will be executed
  end  
  puts 'end of func' # never be executed
end  
3
kuboon