web-dev-qa-db-ja.com

C#のtry / finalyブロックから戻った場合、finallyのコードは常に実行されますか?

いくつかの初期テストのように見えますが、私が知りたいのは、それが保証戻るのか、場合によっては戻れないのかということです。これは私のアプリケーションにとって重要ですが、それが戻らないユースケースはまだ見つかりません。
このテーマに関する専門知識を取得したいのですが。

23
zorg

Finishブロック内のすべては、tryブロックまたはcatchブロック内で何が起こったかに関係なく、常に実行されます。メソッドから戻るかどうかは関係ありません。

これに対する唯一の例外は、finallyブロックのコードが例外をスローした場合、他のコードブロックと同様に実行を停止することです。

Gotoに関しては、答えはまだイエスです。次のコードについて考えてみます。

try
{
    Console.WriteLine("Inside the Try");
    goto MyLabel;
}
finally
{
    Console.WriteLine("Inside the Finally");
}

MyLabel:
    Console.WriteLine("After the Label");

生成される出力は次のとおりです。

トライの内部

最後に中

ラベルの後

25
Dan Rigby

他の回答には多くの不正確さがあります。

制御がtryブロックを離れると、制御はfinallyブロックに渡されます通常-つまり、return、goto、break、continue、または単に終了から外れます。コントロールがtryブロックを離れると、finallyブロックにコントロールが渡されます囲んでいるcatchブロックによってキャッチされた例外を介して

他のすべての状況では、finallyブロックのコードが呼び出されるというguaranteeはありません。特に:

  • Tryブロックコードが無限ループに入った場合、またはスレッドがフリーズしてフリーズ解除されなかった場合、finallyブロックコードは呼び出されません。

  • プロセスがデバッガーで一時停止されてから積極的に強制終了された場合、finallyブロックは呼び出されません。プロセスがフェイルファストを実行する場合、finallyブロックは呼び出されません。

  • 電源コードが壁から引き出された場合、finallyブロックは呼び出されません。

  • 例外がスローされた場合対応するcatchブロックなしの場合、finallyブロックが実行されるかどうかはランタイムの実装の詳細です。キャッチされない例外がある場合、ランタイムは任意の動作を選択できます。 「finallyブロックを実行しない」と「finallyブロックを実行する」はどちらも「任意の動作」の例であるため、どちらかを選択できます。通常、ランタイムが行うことは、finallyブロックが実行される前にデバッガーを接続するかどうかをユーザーに尋ねることです。ユーザーが「いいえ」と言った場合、finallyブロックが実行されます。しかし、繰り返しになりますが、ランタイムはそれを行うために必須ではありません。すぐに失敗する可能性があります。

常に呼び出されるfinallyブロックに依存することはできません。コードの実行について強力な保証が必要な場合は、try-finallyを作成するのではなく、 制約付き実行領域 を作成する必要があります。 CERを正しく記述することは、C#プログラミングで最も難しいタスクの1つであるため、コードを記述する前に、ドキュメントを注意深く調べてください。

ちなみに、最終的にブロックされたgotoに関する「面白い事実」は次のとおりです。

try { goto X; } finally { throw y; } 
X : Console.WriteLine("X");

Xは、到達可能なgotoがターゲットとする到達不能なラベルです。したがって、次にパーティーに参加するときは、「みなさん、到達可能なgotoのターゲットとなる到達不能なラベルを持つC#プログラムを誰かが作成できますか?」のようになります。パーティーの誰が到達可能性の仕様を読んだのか、誰が読んでいないのかがわかります。

46
Eric Lippert

ここではいくつかの例を示します。

Environment.FailFast()

        try
        {
            Console.WriteLine("Try");
            Environment.FailFast("Test Fail");

        }
        catch (Exception)
        {
            Console.WriteLine("catch");
        }
        finally
        {
            Console.WriteLine("finally");
        }

出力は「試してみる」だけです

Stackoverflow

        try
        {
            Console.WriteLine("Try");
            Rec();
        }
        catch (Exception)
        {
            Console.WriteLine("catch");
        }
        finally
        {
            Console.WriteLine("finally");
        }

Recはどこにありますか:

    private static void Rec()
    {
        Rec();
    }

出力は「試行」のみで、StackOverflowが原因でプロセスが終了します。

手渡しの例外

        try
        {
            Console.WriteLine("Try");
            throw new Exception();
        }
        finally
        {
            Console.WriteLine("finally");
        }
7
Lukasz Madon

アプリケーションを終了する致命的な例外の場合Finallyブロックは呼び出されません。スタックオーバーフロー、呼び出すメソッドのJIT中の例外、致命的な例外がCLRランタイムを要求します。

@mintechが指摘しているように、アプリケーションがブロック内でハングした場合、最終的にブロックに到達することはありません。これには、同期オブジェクトの待機、無限ループのデッドロック、またはそれを閉じる方法がないUIも含まれます。

1
Alexei Levenkov