私は最近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
は何に関係なく呼び出されますか?
はい、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
rescue
、ensure
、else
は除外できます。変数を省略することもできます。その場合は、例外処理コードで例外を調べることはできません。例外クラスを除外することもできます。その場合は、StandardError
から継承したすべての例外がキャッチされます。 (Exception
のインスタンスであるがStandardError
のインスタンスではない例外があるため、これが all / exceptionがキャッチされることを意味するわけではないことに注意してください。SystemStackError
、NoMemoryError
、SecurityError
、 NotImplementedError
、LoadError
、SyntaxError
、ScriptError
、Interrupt
、SignalException
、または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);
});
参考までに、rescue
セクションで例外が発生した場合でも、コードの実行が次の例外ハンドラに進む前にensure
ブロックが実行されます。例えば:
begin
raise "Error!!"
rescue
puts "test1"
raise # Reraise exception
ensure
puts "Ensure block"
end
ファイルを確実に閉じるには、File.open
のブロック形式を使用します。
File.open("myFile.txt", "w") do |file|
begin
file << "#{content} \n"
rescue
#handle the error here
end
end
はい、ensure
はどんな状況でも呼ばれます。詳細については、Programming Rubyの本の「 例外、キャッチ、およびスロー 」を参照して、「sure」を検索してください。
はい、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
ステートメントが実行されたかどうかを確認することで、何かが出力されるかどうかを確認できます。
はい、ensure
のようなfinally
ブロックが実行されることを保証します。これは、重要なリソースが確実に保護されるようにするために非常に便利です。エラー時にファイルハンドルを閉じるか、ミューテックスを解放します。
これがensure
が必要な理由です。
def hoge
begin
raise
rescue
raise # raise again
ensure
puts 'ensure' # will be executed
end
puts 'end of func' # never be executed
end