私は友人のためにいくつかのコードをレビューしていますが、彼は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からの引用
最終は、前のtryブロックがexitedであるかどうかに関係なく、コードのステートメントブロックの実行を保証するために使用されます。
一般的には、最終的に実行されます。
次の3つのシナリオでは、最終的に常にが実行されます。
次のシナリオでは、最終的に実行されません。
非同期StackOverflowException。
。NET 2.0以降、スタックオーバーフローによりプロセスが終了します。最終的にCER(Constrained Execution Region)にするためにさらに制約が適用されない限り、finallyは実行されません。 CERは、一般的なユーザーコードでは使用しないでください。クリーンアップコードを常に実行することが重要である場合にのみ使用する必要があります。スタックオーバーフローですべてのプロセスがシャットダウンした後、すべての管理対象オブジェクトはデフォルトでクリーンアップされます。したがって、CERが関連する唯一の場所は、プロセスの外部に割り当てられたリソース(管理されていないハンドルなど)の場合のみです。
通常、アンマネージコードは、ユーザーコードによって消費される前に、なんらかのマネージクラスによってラップされます。マネージラッパークラスは通常、SafeHandleを使用してアンマネージハンドルをラップします。 SafeHandleは、重要なファイナライザーと、クリーンアップコードの実行を保証するためにCERで実行されるReleaseメソッドを実装します。このため、ユーザーコード全体にCERが散らばって表示されることはありません。
そのため、プロセスがとにかく終了するため、最終的にStackOverflowExceptionで実行されないという事実はユーザーコードに影響を与えません。 SafeHandleまたはCriticalFinalizerObjectの外部にある管理されていないリソースをクリーンアップする必要があるEdgeのケースがある場合は、次のようにCERを使用します。ただし、これは悪い習慣であることに注意してください。アンマネージコンセプトは、設計によりマネージクラスと適切なSafeHandleに抽象化する必要があります。
例えば。、
// 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年間プログラミングした後、私は知らなかったとは信じられません。
anyの例外をスローまたはトリガーした場合、catch
ブロック内でソートします(奇妙なStackOverflowExceptions
とその同類のものだけではありません) )、およびtry/catch/finally
ブロック全体が別のtry/catch
ブロック内にない場合、finally
ブロックは実行されません。これは簡単に実証されます-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();
}
これには理由があると確信していますが、あまり広く知られていないのは奇妙です。 (たとえば here と書かれていますが、この特定の質問のどこにも書かれていません。)
私はパーティーに遅れていますが、実際には例外がスローされるシナリオ(OPの例とは異なります)でMSDNステート( https://msdn.Microsoft.com/en-us/library/zwc8s4fz .aspx ): "例外がキャッチされない場合、finallyブロックの実行は、オペレーティングシステムが例外のアンワインドをトリガーするかどうかに依存します操作。"
Finallyブロックは、コールスタックのさらに他の関数(Mainなど)が例外をキャッチした場合にのみ実行されるguaranteedです。すべてのランタイム環境(CLRおよびOS)C#プログラムは、プロセスが終了するときに所有するほとんどのリソース(ファイルハンドルなど)で実行されるため、この詳細は通常問題になりません。ただし、場合によっては重要なこともあります。データベース操作の途中で、respをコミットしたい場合。ほどきます。または、OSによって自動的に閉じられず、サーバーをブロックするリモート接続。
はい。それが実際、finallyステートメントの要点です。壊滅的な事態(メモリ不足、コンピューターの取り外しなど)が発生しない限り、finallyステートメントを常に実行する必要があります。
また、キャッチされていない例外やWindowsサービスでホストされているスレッドで実行されている場合も起動しません
system.exit(0)を使用してアプリケーションを終了する場合に備えて、最終的には実行されません。のように
try
{
System.out.println("try");
System.exit(0);
}
finally
{
System.out.println("finally");
}
結果は次のようになります。試してみてください
はい最後に電話してください。
public static bool someMethod()
{
try
{
return true;
throw new Exception("test"); // doesn't seem to get executed
}
finally
{
//code in question
}
}
シナリオの99%では、finally
ブロック内のコードが実行されることが保証されますが、このシナリオを考えてください:try
-> finally
ブロックを持つスレッドがあります(catch
なし)、そのスレッド内で未処理の例外が発生します。この場合、スレッドは終了し、そのfinally
ブロックは実行されません(この場合、アプリケーションは引き続き実行できます)
このシナリオは非常にまれですが、答えが常に「はい」ではなく、ほとんどの場合「はい」であり、まれに「いいえ」であることを示すためだけです。
Finallyブロックの主な目的は、その中に記述されているものをすべて実行することです。 tryまたはcatchで発生することに依存するべきではありませんが、System.Environment.Exit(1)を使用すると、アプリケーションはコードの次の行に移動せずに終了します。