Rubyで始めて、救って、確認しますか?


547

最近Rubyでプログラミングを始めて、例外処理を検討しています。

ensureRuby finallyがC#に相当するのかどうか疑問に思っていましたか?私が持っているべきです:

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

DOESは、ensure例外が発生していない場合でも、何に関係なく呼び出されますか?


1
どちらも良くない。原則として、外部リソースを処理するときは、常にリソースの開始 `をbeginブロック内に置く必要があります。
Nowaker 2018年

回答:


1181

はい、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

あなたは省くことができrescueensureまたはelse。変数を省略することもできます。その場合、例外処理コードで例外を検査できなくなります。(ええと、いつでもグローバル例外変数を使用して、発生した最後の例外にアクセスできますが、少しハッキーです。)そして、例外クラスを省略できます。この場合、継承元のすべての例外StandardErrorがキャッチされます。これはことを意味しないことを(してくださいノートすべてのインスタンスである例外があるので、例外がキャッチされているExceptionではないがStandardError。のようなプログラムの妥協整合性大抵非常に厳しい例外SystemStackErrorNoMemoryErrorSecurityErrorNotImplementedErrorLoadErrorSyntaxErrorScriptErrorInterruptSignalExceptionまたはSystemExit。)

一部のブロックは、暗黙の例外ブロックを形成します。たとえば、メソッド定義は暗黙的に例外ブロックでもあるため、次のように記述するのではなく

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

あなたはただ書く

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

または

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

class定義やmodule定義についても同様です。

しかし、あなたが尋ねている特定のケースでは、実際にはもっと良いイディオムがあります。一般に、最後にクリーンアップする必要があるリソースを操作する場合は、すべてのクリーンアップを実行するメソッドにブロックを渡すことでそれを行います。これusingはC#のブロックに似ていますが、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。ただし、これは、任意の種類のリソースクリーンアップ(usingC#でのアラ)またはトランザクションなど、考えられるあらゆるものを実装するために、独自のコードでも使用できる一般的なパターンです。

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


ところで、最近のC#では、usingRubyスタイルのリソースブロックを自分で実装できるため、実際には不要です。

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);
});

81
ensureステートメントは最後に実行されますが、戻り値ではないことに注意してください。
Chris

30
SOでこのような豊かな貢献を見るのが大好きです。それは、OPが求めたものを超えて、より多くの開発者に適用されるようになっていますが、まだ話題になっています。この回答と編集からいくつかのことを学びました。「はい、ensure何があっても呼ばれます」とだけでなく、ありがとうございました。
デニス

3
確認が完了することは保証されないことに注意してください。スレッド内でbegin / ensure / endが存在する場合を想定し、ensureブロックの最初の行が呼び出されているときにThread.killを呼び出します。これにより、残りの保障が実行されなくなります。
Teddy

5
@テディ:確実に実行が開始されることが保証され、完了が保証されるわけではありません。あなたの例はやりすぎです。ensureブロック内の単純な例外により、ブロックも終了します。
Martin Konecny、2015年

3
また、確実に呼び出されるという保証はありません。私は真剣です。停電/ハードウェアエラー/ OSクラッシュが発生する可能性があり、ソフトウェアが重要な場合は、それも考慮する必要があります。
EdvardM、2015年

37

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

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

14

ファイルを確実に閉じたい場合は、次のブロック形式を使用する必要がありますFile.open

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

3
エラーを処理したくないが、それを発生させてファイルハンドルを閉じるだけなら、ここでbeginレスキューは必要ありませんか?
ロジャーパック2013


5

はい、ensure毎回実行されることを保証するためfile.closebeginブロック内のは必要ありません。

ちなみに、テストする良い方法は次のようにすることです:

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

例外が発生したときに「========= inside sure block」が出力されるかどうかをテストして確認できます。次に、エラーを発生させるステートメントをコメントアウトし、ensure何かが出力されるかどうかを確認することにより、ステートメントが実行されるかどうかを確認できます。


4

これが私たちが必要とする理由ですensure

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

4

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


彼/彼女の場合を除いて、File.open一部がbegin-ensureブロック内にないため、ファイルが閉じられる保証はありません。だけfile.closeですが、それは十分ではありません。
Nowaker、2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.