Tryブロックで値を返すと、Finallyステートメントのコードが起動しますか?
-
19-08-2019 - |
質問
友人のためにいくつかのコードをレビューしていて、try-finallyブロック内でreturnステートメントを使用していたと言います。 tryブロックの残りが起動しなくても、Finallyセクションのコードは起動しますか?
例:
public bool someMethod()
{
try
{
return true;
throw new Exception("test"); // doesn't seem to get executed
}
finally
{
//code in question
}
}
解決
簡単な答え:はい。
他のヒント
通常、はい。 finallyセクションは、例外やreturnステートメントなど、何が起きても実行されることが保証されています。このルールの例外は、スレッドで発生する非同期例外です(OutOfMemoryException
、StackOverflowException
)。
そのような状況での非同期例外と信頼できるコードの詳細については、制約された実行領域。
ここに小さなテストがあります:
class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("before");
Console.WriteLine(test());
Console.WriteLine("after");
}
static string test()
{
try
{
return "return";
}
finally
{
Console.WriteLine("finally");
}
}
}
結果は次のとおりです。
before
finally
return
after
MSDNからの引用
finally は、前の try ブロックの終了の方法に関係なく、コードのステートメントブロックの実行を保証するために使用されます。
はい、最終的に実行されます。
次の3つのシナリオでは、最終的に常に実行されます:
- 例外は発生しません
- 同期例外(通常のプログラムフローで発生する例外)。
これには、System.Exceptionから派生するCLS準拠の例外と、System.Exceptionから派生しない非CLS準拠の例外が含まれます。非CLS準拠の例外は、RuntimeWrappedExceptionによって自動的にラップされます。 C#は非CLS苦情例外をスローできませんが、C ++などの言語はスローできます。 C#は、非CLS準拠の例外をスローできる言語で記述されたコードを呼び出す可能性があります。 - 非同期ThreadAbortException
.NET 2.0の時点では、ThreadAbortExceptionがfinalの実行を妨げなくなりました。 ThreadAbortExceptionは、finallyの前または後に持ち上げられます。最終的には常に実行され、スレッドの中断が発生する前に実際に試行が入力されている限り、スレッドの中断によって中断されません。
次のシナリオでは、最終的に実行されません:
非同期StackOverflowException。
.NET 2.0以降、スタックオーバーフローによりプロセスが終了します。最終的にCER(Constrained Execution Region)にするためにさらに制約が適用されない限り、finallyは実行されません。 CERは、一般的なユーザーコードでは使用しないでください。クリーンアップコードを常に実行することが重要である場合にのみ使用する必要があります。すべてのプロセスがスタックオーバーフローでシャットダウンした後、すべての管理対象オブジェクトはデフォルトでクリーンアップされます。したがって、CERが関連する唯一の場所は、プロセスの外部に割り当てられているリソース(管理されていないハンドルなど)です。
通常、アンマネージコードは、ユーザーコードによって消費される前に、何らかのマネージクラスによってラップされます。マネージラッパークラスは通常、SafeHandleを使用してアンマネージハンドルをラップします。 SafeHandleは、重要なファイナライザと、クリーンアップコードの実行を保証するためにCERで実行されるReleaseメソッドを実装します。このため、ユーザーコード全体にCERが散らばって表示されることはありません。
したがって、StackOverflowExceptionで最終的に実行されないという事実は、プロセスがとにかく終了するため、ユーザーコードには影響を与えません。 SafeHandleまたはCriticalFinalizerObjectの外部にある、管理されていないリソースをクリーンアップする必要があるエッジケースがある場合は、次のようにCERを使用します。ただし、これは悪い習慣です。アンマネージコンセプトは、設計によりマネージクラスと適切なSafeHandleに抽象化する必要があります。
e.g。、
// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// This is *NOT* a CER
}
finally
{
// This is a CER; guaranteed to run, if the try was entered,
// even if a StackOverflowException occurs.
}
これには非常に重要な例外がありますが、他の回答では言及されていませんが、(C#で18年間プログラミングした後)私は知らなかったとは信じられません。
catch
ブロック内で any ソートの例外をスローまたはトリガーした場合(奇妙なStackOverflowExceptions
とその同類のものだけでなく)、<= >別のtry/catch/finally
ブロック内のブロック、try/catch
ブロックは実行されません。これは簡単に実証されます-そして、finally
ブロックが実行されない原因となるのは本当に奇妙で小さなコーナーケースだと何度も読んだので、自分で見ていなかったら、信じられなかったでしょうそれ。
static void Main(string[] args)
{
Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
try
{
Console.WriteLine("Inside try but before exception.");
throw new Exception("Exception #1");
}
catch (Exception ex)
{
Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
throw;
}
finally
{
Console.WriteLine("This never gets executed, and that seems very, very wrong.");
}
Console.WriteLine("This never gets executed, but I wasn't expecting it to.");
Console.ReadLine();
}
これには理由があると確信していますが、あまり広く知られていないのは奇妙です。 (たとえばここに記載されていますが、この特定の質問のどこにも記載されていません。)
私はパーティーに遅れていることに気付きますが、実際には例外がスローされるシナリオ(OPの例とは異なります)でMSDN状態( https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx ): <!> quot;例外がキャッチされない場合、finallyブロックの実行は、オペレーティングシステムが例外のアンワインド操作をトリガーすることを選択したかどうかに依存します。<!> quot;
finallyブロックは、コールスタックのさらに他の関数(Mainなど)が例外をキャッチした場合にのみ実行される保証です。すべてのランタイム環境(CLRおよびOS)C#プログラムは、プロセスが終了するときに所有するほとんどのリソース(ファイルハンドルなど)で実行されるため、この詳細は通常問題になりません。ただし、場合によっては重要なこともあります。データベース操作が進行中で、respをコミットします。ほどきます。または、OSによって自動的に閉じられず、サーバーをブロックするリモート接続。
はい。それが実際、finallyステートメントの要点です。何か致命的な事態(メモリ不足、コンピューターの取り外しなど)が発生しない限り、finallyステートメントを常に実行する必要があります。
また、キャッチされていない例外やWindowsサービスでホストされているスレッドで実行されている場合も起動しません
を使用してアプリケーションを終了する場合、最終的には実行されません System.exit(0);
のようにtry
{
System.out.println("try");
System.exit(0);
}
finally
{
System.out.println("finally");
}
結果は次のようになります。 試します
99%のシナリオでは、finally
ブロック内のコードが実行されることが保証されますが、このシナリオを考えてください:try
-<!> gt; catch
を持つスレッドがありますブロック(<=>なし)すると、そのスレッド内で未処理の例外が発生します。この場合、スレッドは終了し、その<=>ブロックは実行されません(この場合、アプリケーションは実行を継続できます)
このシナリオは非常にまれですが、答えが常に<!> quot; Yes <!> quot;ではないことを示すためだけです。ほとんどの場合<!> quot; Yes <!> quot;まれに、まれに<!> quot;いいえ<!> quot;。
finallyブロックの主な目的は、その中に記述されているものをすべて実行することです。 tryまたはcatchで発生することに依存するべきではありませんが、System.Environment.Exit(1)を使用すると、アプリケーションは次のコード行に移動せずに終了します。