catchブロックから例外をスローした場合、最終的にいつ実行されますか?


135
try {
   // Do stuff
}
catch (Exception e) {
   throw;
}
finally {
   // Clean up
}

上記のブロックで、finallyブロックが呼び出されるのはいつですか?eを投げる前、または最後に呼び出されてから、キャッチする?


14
あなたが「eを投げる」べきではないps。それは元の例外のスタックトレースを台無しにするからです。あなたはただ「投げる」べきです。または、新しい例外を作成し、それをスローする前にInnerExceptionを "e"に設定します。
Erv Walter、

24
最後に実行されなかった場合、最後にキーワードの選択はかなり貧弱になるでしょうね。
Eric Lippert、

@ErvWalterはまだ本当ですか?私はそれをVS2017で両方の方法でテストしていますが、まったく同じように見えます。さらに詳しい情報やリファレンスを提供していただけますか?ありがとう
Jeff Puckett 2017年

提案に名前を付けるだけで、例外ex-イベント/デリゲート用にeを予約
mr R

回答:


138

eが再スローされた後(つまり、catchブロックが実行された後)に呼び出されます。

7年後のこの編集-重要な注意点の1つはe、try / catchブロックでキャッチされない場合、またはコールスタックの上位で処理されなかったり、グローバル例外ハンドラーで処理されなかった場合finallyブロックまったく実行されない可能性があることです。


18
Envrionment.FailFast()を呼び出した場合は決して発生しない
ヨハネスルドルフ

16
ブランドンの答えでコードを試した後、私はそれを参照してfinally、前にスローされた例外があれば実行されていないcatch巻き込まない外にtry- catchブロック!
Andrew

3
(承認された)回答を編集して新しい情報を含めていただき、ありがとうございます。
Gordon Bean

3
彼ら(Microsoft)は新しいドキュメントサイトdocs.microsoft.com/en-us/dotnet/csharp/language-reference/…でそのことについて話し ます。 、例外が処理されない場合、finallyブロックの実行は、例外の巻き戻し操作がどのようにトリガーされるかに依存します。これは、コンピュータの設定に依存します。」
DotNetSparky

1
「呼び出しスタックのさらに上のtry / catchブロックでキャッチ」には、ASP.NETやテストランナーなどのフレームワークハンドラーが含まれることに注意してください。それを置くより良い方法は、「c​​atchブロックの後でプログラムが引き続き実行される場合、finallyブロックが実行される」ことです。
ArrowCase、

91

試してみませんか:

outer try
inner try
inner catch
inner finally
outer catch
outer finally

コード付き(垂直スペース用にフォーマット済み):

static void Main() {
    try {
        Console.WriteLine("outer try");
        DoIt();
    } catch {
        Console.WriteLine("outer catch");
        // swallow
    } finally {
        Console.WriteLine("outer finally");
    }
}
static void DoIt() {
    try {
        Console.WriteLine("inner try");
        int i = 0;
        Console.WriteLine(12 / i); // oops
    } catch (Exception e) {
        Console.WriteLine("inner catch");
        throw e; // or "throw", or "throw anything"
    } finally {
        Console.WriteLine("inner finally");
    }
}

5
+1、とてもシンプルなものなら、Marcのように試してみてください。GJは、入れ子のtry / catch / finallyでそれを示しています:)
アレンライス

1
@AllenRiceなぜMarcがすでに持っているのならそれを試してみてください、そして私は単にGoogleでMarcの答えを見つけることができますか?または、もっと良い方法として、自分で試してみて、SOの質問を作成して、他の人のために自分で答えてください。
joshden 2015年

8
外側のキャッチで例外をキャッチしないと、最終的に内側は決して実行されないことに注意してください!! その場合、出力はouter try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew

1
@Andrewあなたは正しい。あなたがここに見つけることができます説明MSDNマガジン2008年9月:CLRでの未処理の例外処理(オープンCHMには、それがロックを解除する必要があります。ファイルのプロパティ] - >一般- >ロック解除)。CLRが「例外の処理に同意した例外ハンドラー」DivideByZeroExceptionを見つけられないため、外側のcatchブロックを「catch(ArgumentException)」で置き換えると、最終的にブロックされません。
vladimir 2017

35

ここですべての回答を読んだ後、最終的な回答は次のようになります

  • catchブロック内で例外を再スローし、その例外が別のcatchブロック内でキャッチされた場合、ドキュメントに従ってすべてが実行されます。

  • ただし、再発生した例外が処理されない場合、finallyは実行されません。

このコードサンプルをVS2010 w / C#4.0でテストしました

static void Main()
    {
        Console.WriteLine("Example 1: re-throw inside of another try block:");

        try
        {
            Console.WriteLine("--outer try");
            try
            {
                Console.WriteLine("----inner try");
                throw new Exception();
            }
            catch
            {
                Console.WriteLine("----inner catch");
                throw;
            }
            finally
            {
                Console.WriteLine("----inner finally");
            }
        }
        catch
        {
            Console.WriteLine("--outer catch");
            // swallow
        }
        finally
        {
            Console.WriteLine("--outer finally");
        }
        Console.WriteLine("Huzzah!");

        Console.WriteLine();
        Console.WriteLine("Example 2: re-throw outside of another try block:");
        try
        {
            Console.WriteLine("--try");
            throw new Exception();
        }
        catch
        {
            Console.WriteLine("--catch");
            throw;
        }
        finally
        {
            Console.WriteLine("--finally");
        }

        Console.ReadLine();
    }

出力は次のとおりです。

例1:別のtryブロックの内部で再スローする:--
outer try
---- inner try
---- inner catch
---- inner finally
--outer catch
--outer finally
Huzzah!

例2:別のtryブロックの外で再スローする:--
try
--catch

未処理の例外:System.Exception:タイプ 'System.Exception'の例外がスローされました。
C:\ local source \ ConsoleApplication1 \ Program.cs:line 53のConsoleApplication1.Program.Main()で


3
素晴らしいキャッチ、私はそれを知らなかった!
Andrew

1
選択した内容によっては、最後に最後に実行される場合があります:stackoverflow.com/a/46267841/480982
Thomas Weller

1
興味深い... .NET Core 2.0では、finally部分は未処理の例外の後に実行されます。
Mahdi Ghiasi

興味深いことに、私は.NET Core 2.0と.NET Framework 4.6.1の両方でテストを実行しましたが、どちらも未処理の例外の後に最終的に実行されます。この動作は変更されましたか?
Cameron Bielstein、2018

24

あなたの例はこのコードと同じように動作します:

try {
    try {
        // Do stuff
    } catch(Exception e) {
        throw e;
    }
} finally {
    // Clean up
}

補足として、本当に意味する場合throw e;(つまり、キャッチしたのと同じ例外をスローする場合)は、新しいスタックトレースを作成する代わりに、元のスタックトレースを保持するため、を実行する方がはるかに優れていthrow;ます。


これは正しいとは思いません。最後に、外側のtryブロックの内側ではなく外側にする必要があります
マシューピグラム14

@MatthewPigram:どういう意味ですか?finallyブロックは、実際には後に実行されますcatch私のスニペットを説明しようとしているものであるブロック(catchブロックが例外を再スローした場合でも)、。
Daniel Pryden 2014

私が彼の例をどのように解釈するかから、彼は最終的に別のtryブロック内でtry catchを実行しようとしています。いよいよトライキャッチ内のトライキャッチではない
マシューピグラム

1
@MatthewPigram:私の答えには、「try-catch-finally」構成がまったくありません。「トライ・ファイナル」があり、そのtryブロックの内側に「トライ・キャッチ」があります。2つの2部構成を使用して、3部構成の動作を説明しようとしています。try元の質問には2番目のブロックの兆候が見当たらないので、どこでそれが得られているのかわかりません。
Daniel Pryden 14

12

キャッチハンドラブロック内に未処理の例外がある場合、finallyブロックは正確に0回呼び出されます

  static void Main(string[] args)
  {
     try
     {
        Console.WriteLine("in the try");
        int d = 0;
        int k = 0 / d;
     }
     catch (Exception e)
     {
        Console.WriteLine("in the catch");
        throw;
     }
     finally
     {
        Console.WriteLine("In the finally");
     }
  }

出力:

C:\ users \ administrator \ documents \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

試してみて

キャッチで

未処理の例外:System.DivideByZeroException:ゼロで除算しようとしました。TestExceptionNesting.Program.Main(String [] args)at C:\ users \ administrator \ documents \ TestExceptionNesting \ TestExceptionNesting.cs:line 22

C:\ users \ administrator \ documents \ TestExceptionNesting \ bin \ release>

今日、この質問はインタビューで聞かれましたが、インタビュアーは「最後に呼び出されないのは確かですか?」トリックの質問なのか、インタビュアーが何か他のことを考えていて、デバッグするために間違ったコードを書いたのかどうかわからなかったので、帰宅してそれを試してみました(ビルドと実行、デバッガーの相互作用なし)。残り。


スローされた例外がスタックの上位の別のキャッチにキャッチされない限り、その場合、スローされた例外が処理されると実行される可能性があります...または、私は間違っているかもしれません...
tomosius

@tomosius、はい、それがブランドンの答えが説明するものです。:)
Andrew

@tomosiusそのため、「未処理の例外がある場合」を指定することから始めました。スローされた例外がどこかでキャッチされた場合、定義により別のケースについて話していることになります。
エウゼビオルフィアンジルバーマン2017年

本当じゃない。少なくともNET Core 3.1は対象外です。このコードを使用した単純な新しいコンソールプロジェクトは、例外の後に「最終的に」を示しています。
emzero

興味深いことに、動作が変更され、投稿したときに.NETコアさえ存在しませんでした;)
Eusebio Rufian-Zilbermann

2

また、コードをデバッグして、finallyが呼び出されたときに通知することも簡単な方法です。


1

C#コンソールアプリケーションでのテストでは、例外がスローされた後、最終的にコードが実行されました。「アプリケーションエラーダイアログ」が存在し、「プログラムを閉じる」オプションを選択した後、そのコンソールウィンドウでfinallyブロックが実行されました。しかし、finallyコードブロック内にブレークポイントを設定すると、私はそれにぶつかることはありません。デバッガーは、throwステートメントで停止し続けます。これが私のテストコードです:

    class Program
    {
       static void Main(string[] args)
       {
          string msg;
          Console.WriteLine(string.Format("GetRandomNuber returned: {0}{1}", GetRandomNumber(out msg), msg) == "" ? "" : "An error has occurred: " + msg);
       }

       static int GetRandomNumber(out string errorMessage)
       {
         int result = 0;
         try
         {
            errorMessage = "";
            int test = 0;
            result = 3/test;
            return result;
         }
         catch (Exception ex)
         {
            errorMessage = ex.Message;
            throw ex;

         }
         finally
         {
            Console.WriteLine("finally block!");
         }

       }
    }

VS2010でのデバッグ-.NET Framework 4.0

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.