web-dev-qa-db-ja.com

Try /最後に(キャッチなしで)例外をバブルしますか?

私は答えがイエスであることをほぼ確信しています。 Try finallyブロックを使用し、Catchブロックを使用しない場合、例外が発生します。正しい?

一般的な慣行についての考えはありますか?

セス

104
Seth Spearman

はい、そうです。もちろん、finallyブロックは例外をスローしないと仮定します。この場合、元のスローされたブロックを効果的に「置換」します。

117
Jon Skeet

一般的な慣行についての考えはありますか?

はい。 注意。 finallyブロックの実行中は、未処理の予期しない例外がスローされたであるため、実行されている可能性があります。つまり、何かが壊れたであり、完全に予期しないものが発生している可能性があります。

そのような状況では、おそらく最終的にブロックでコードを実行しないでください。 finallyブロックのコードは、依存するサブシステムが健全であると仮定するように構築されている場合がありますが、実際にはそれらが深く壊れている可能性があります。 finallyブロックのコードは事態を悪化させる可能性があります。

たとえば、私はこのようなことをよく見ます:

DisableAccessToTheResource();
try
{
    DoSomethingToTheResource();
}
finally
{
    EnableAccessToTheResource();
}

このコードの作者は、「私は世界の状態に一時的な突然変異を起こしている。私は呼ばれる前の状態に状態を復元する必要がある」と考えています。しかし、これがうまくいかない可能性のあるすべての方法について考えてみましょう。

まず、リソースへのアクセスは、呼び出し元によって既に無効にされている可能性があります。その場合、このコードはそれを再度有効にします。

第二に、DoSomethingToTheResourceが例外をスローした場合、リソースへのアクセスを有効にするために正しいことですか?リソースを管理するコードは予期しない破損です。このコードは、実際には「管理コードが壊れている場合、他のコードが壊れたコードをできるだけ早く呼び出して、ひどく失敗する可能性があることを確認してください」と述べています。これは悪い考えのようです。

3番目に、DoSomethingToTheResourceが例外をスローした場合、EnableAccessToTheResourceも例外をスローしないことをどのように知ることができますか?リソースの使用がひどいものであっても、クリーンアップコードに影響を与える可能性があります。その場合、元の例外は失われ、問題の診断が難しくなります。

Try-finallyブロックを使用せずに、次のようなコードを書く傾向があります。

bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
    DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
    EnableAccessToTheResource();

必要な場合を除き、状態は変更されません。これで、呼び出し元の状態が混乱することはありません。そして今、DoSomethingToTheResourceが失敗した場合、アクセスを再度有効にしません。何かが深く壊れていると想定しており、コードを実行し続けることで状況を悪化させる危険はありません。可能であれば、呼び出し元に問題を処理させます。

それでは、いつfinallyブロックを実行するのが良いアイデアですか?まず、例外が予想されるとき。たとえば、他の誰かがファイルをロックしているため、ファイルをロックしようとすると失敗することがあります。その場合、例外をキャッチしてユーザーに報告するのが理にかなっています。その場合、何が壊れているかについての不確実性が減少します。クリーンアップすることで事態を悪化させることはほとんどありません。

次に、クリーンアップするリソースが不足しているシステムリソースの場合。たとえば、finallyブロックのファイルハンドルを閉じることは理にかなっています。 (もちろん、「使用」は、try-finallyブロックを記述するための単なる別の方法です。)ファイルの内容が破損している可能性がありますが、それについて今できることは何もありません。ファイルハンドルは最終的に閉じられる予定なので、遅くなるよりも早くなる可能性があります。

57
Eric Lippert