web-dev-qa-db-ja.com

Tryブロックで値を返すと、Finallyステートメントのコードが起動しますか?

私は友人のためにいくつかのコードをレビューしていますが、彼は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
  }
}
221
JamesEggers

簡単な答え:はい。

249
Andrew Rollings

通常、はい。 finallyセクションは、例外やreturnステートメントなど、何が起きても実行されることが保証されています。この規則の例外は、スレッドで発生する非同期例外です(OutOfMemoryExceptionStackOverflowException)。

そのような状況での非同期例外と信頼できるコードの詳細については、 制約された実行領域 を参照してください。

197
Mehrdad Afshari

ここに小さなテストがあります:

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
148
Jon B

MSDNからの引用

最終は、前のtryブロックがexitedであるかどうかに関係なく、コードのステートメントブロックの実行を保証するために使用されます。

36
Perpetualcoder

一般的には、最終的に実行されます。

次の3つのシナリオでは、最終的に常にが実行されます。

  1. 例外は発生しません
  2. 同期例外(通常のプログラムフローで発生する例外)。
    これには、System.Exceptionから派生するCLS準拠の例外と、System.Exceptionから派生しない非CLS準拠の例外が含まれます。非CLS準拠の例外は、RuntimeWrappedExceptionによって自動的にラップされます。 C#は非CLS苦情例外をスローできませんが、C++などの言語はスローできます。 C#は、非CLS準拠の例外をスローできる言語で記述されたコードを呼び出す可能性があります。
  3. 非同期ThreadAbortException
    。NET 2.0以降、ThreadAbortExceptionによって最終的に実行が妨げられることはなくなりました。 ThreadAbortExceptionは、finallyの前または後に持ち上げられます。最終的には常に実行され、スレッドアボートが発生する前に実際に試行が入力されている限り、スレッドアボートによって中断されません。

次のシナリオでは、最終的に実行されません。

非同期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.
}
19
markn

これには非常に重要な例外がありますが、これは他の回答で言及されていなかったものであり、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 と書かれていますが、この特定の質問のどこにも書かれていません。)

9
Ken Smith

私はパーティーに遅れていますが、実際には例外がスローされるシナリオ(OPの例とは異なります)でMSDNステート( https://msdn.Microsoft.com/en-us/library/zwc8s4fz .aspx ): "例外がキャッチされない場合、finallyブロックの実行は、オペレーティングシステムが例外のアンワインドをトリガーするかどうかに依存します操作。"

Finallyブロックは、コールスタックのさらに他の関数(Mainなど)が例外をキャッチした場合にのみ実行されるguaranteedです。すべてのランタイム環境(CLRおよびOS)C#プログラムは、プロセスが終了するときに所有するほとんどのリソース(ファイルハンドルなど)で実行されるため、この詳細は通常問題になりません。ただし、場合によっては重要なこともあります。データベース操作の途中で、respをコミットしたい場合。ほどきます。または、OSによって自動的に閉じられず、サーバーをブロックするリモート接続。

7

はい。それが実際、finallyステートメントの要点です。壊滅的な事態(メモリ不足、コンピューターの取り外しなど)が発生しない限り、finallyステートメントを常に実行する必要があります。

3

また、キャッチされていない例外やWindowsサービスでホストされているスレッドで実行されている場合も起動しません

Windowsサービスで実行されているスレッドでは最終的に実行されません

3
Stig

system.exit(0)を使用してアプリケーションを終了する場合に備えて、最終的には実行されません。のように

try
{
    System.out.println("try");
    System.exit(0);
}
finally
{
   System.out.println("finally");
}

結果は次のようになります。試してみてください

2
Hakuna Matata

はい最後に電話してください。

  public static bool someMethod()
    {
        try
        {

            return true;
            throw new Exception("test"); // doesn't seem to get executed
        }
        finally
        {
            //code in question
        }
    }
0

シナリオの99%では、finallyブロック内のコードが実行されることが保証されますが、このシナリオを考えてください:try-> finallyブロックを持つスレッドがあります(catchなし)、そのスレッド内で未処理の例外が発生します。この場合、スレッドは終了し、そのfinallyブロックは実行されません(この場合、アプリケーションは引き続き実行できます)

このシナリオは非常にまれですが、答えが常に「はい」ではなく、ほとんどの場合「はい」であり、まれに「いいえ」であることを示すためだけです。

0
Jonathan Perry

Finallyブロックの主な目的は、その中に記述されているものをすべて実行することです。 tryまたはcatchで発生することに依存するべきではありませんが、System.Environment.Exit(1)を使用すると、アプリケーションはコードの次の行に移動せずに終了します。

0
Ashish Sahu