回答:
はい、そうです。finally
もちろん、ブロックが例外をスローしないことを前提としています。その場合、最初にスローされたブロックが事実上「置き換え」られます。
一般的な実践についての考えはありますか?
はい。気をつけて。最終的にブロックが実行されている場合、未処理の予期しない例外がスローされているため、実行されている可能性があります。つまり、何かが壊れており、まったく予期しないことが起こっている可能性があります。
そのような状況では、間違いなく、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ブロックを書き込む別の方法にすぎません。)ファイルの内容が破損している可能性がありますが、今はそれに対してできることは何もありません。ファイルハンドルは最終的に閉じられるため、遅くなるよりも早くなる可能性があります。