299

finallyブロックが例外をスローした場合、正確には何が起こりますか?

具体的には、finallyブロックの途中で例外がスローされた場合はどうなりますか。このブロックの残りのステートメント(後)は呼び出されますか?

例外が上向きに伝播することを認識しています。

4

11 に答える 11

450

finallyブロックが例外をスローした場合、正確には何が起こりますか?

その例外は伝播し、より高いレベルで処理されます(可能です)。

例外がスローされたポイントを超えて、finallyブロックが完了することはありません。

以前の例外の処理中にfinallyブロックが実行されていた場合、その最初の例外は失われます。

C#4言語仕様§8.9.5:finallyブロックが別の例外をスローした場合、現在の例外の処理は終了します。

于 2010-05-26T08:26:36.173 に答える
111

このような質問については、通常、Visual Studioで空のコンソールアプリケーションプロジェクトを開き、小さなサンプルプログラムを作成します。

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Inner catch block handling {0}.", ex.Message);
                throw;
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

プログラムを実行するとcatchfinallyブロックが実行される正確な順序が表示されます。例外がスローされた後のfinallyブロックのコードは実行されないことに注意してください(実際、このサンプルプログラムでは、Visual Studioは到達不能コードを検出したことを警告します)。

tryブロックからスローされた内部catchブロック処理例外。
内側がついにブロック
finallyブロックからスローされた外部キャッチブロック処理例外。
アウターブロック

追記

Michael Damatovが指摘したtryように、(内側の)ブロックで処理しない場合、ブロックからの例外は「食べられる」ことになりますcatch。実際、上記の例では、再スローされた例外は外側のキャッチブロックに表示されません。これをさらに明確にするために、次のわずかに変更されたサンプルを見てください。

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

出力からわかるように、内部例外は「失われました」(つまり無視されます)。

内側がついにブロック
finallyブロックからスローされた外部キャッチブロック処理例外。
アウターブロック
于 2010-05-26T08:32:31.540 に答える
10

保留中の例外がある場合 (tryブロックに はあるfinallyが がない場合catch)、新しい例外がその例外を置き換えます。

保留中の例外がない場合は、finallyブロックの外側で例外をスローするのと同じように機能します。

于 2010-05-26T08:36:13.450 に答える
6

元の例外の方が重要な場合に備えて、 「元の例外」(ブロックでスロー) を保存しtry、「最終的に例外」(ブロックでスロー) を犠牲にするための簡単な (そしてかなり明白な) スニペット:finally

try
{
    throw new Exception("Original Exception");
}
finally
{
    try
    {
        throw new Exception("Finally Exception");
    }
    catch
    { }
}

上記のコードが実行されると、「元の例外」がコール スタックに伝播し、「最終的に例外」が失われます。

于 2016-05-09T22:55:43.580 に答える
5

例外が伝播されます。

于 2010-05-26T08:26:06.220 に答える
2

別の例外がアクティブなときに例外をスローすると、最初の例外が2番目の(後の)例外に置き換えられます。

これが何が起こるかを説明するいくつかのコードです:

    public static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("first exception");
            }
            finally
            {
                //try
                {
                    throw new Exception("second exception");
                }
                //catch (Exception)
                {
                    //throw;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
  • コードを実行すると、「2番目の例外」が表示されます
  • tryステートメントとcatchステートメントのコメントを解除すると、「最初の例外」が表示されます
  • また、スローのコメントを解除します。ステートメントを実行すると、「2番目の例外」が再び表示されます。
于 2012-11-29T06:25:56.770 に答える
2

数ヶ月前、私もこのようなことに直面しました。

    private  void RaiseException(String errorMessage)
    {
        throw new Exception(errorMessage);
    }

    private  void DoTaskForFinally()
    {
        RaiseException("Error for finally");
    }

    private  void DoTaskForCatch()
    {
        RaiseException("Error for catch");
    }

    private  void DoTaskForTry()
    {
        RaiseException("Error for try");
    }


        try
        {
            /*lacks the exception*/
            DoTaskForTry();
        }
        catch (Exception exception)
        {
            /*lacks the exception*/
            DoTaskForCatch();
        }
        finally
        {
            /*the result exception*/
            DoTaskForFinally();
        }

このような問題を解決するために、次のようなユーティリティ クラスを作成しました

class ProcessHandler : Exception
{
    private enum ProcessType
    {
        Try,
        Catch,
        Finally,
    }

    private Boolean _hasException;
    private Boolean _hasTryException;
    private Boolean _hasCatchException;
    private Boolean _hasFinnallyException;

    public Boolean HasException { get { return _hasException; } }
    public Boolean HasTryException { get { return _hasTryException; } }
    public Boolean HasCatchException { get { return _hasCatchException; } }
    public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
    public Dictionary<String, Exception> Exceptions { get; private set; } 

    public readonly Action TryAction;
    public readonly Action CatchAction;
    public readonly Action FinallyAction;

    public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
    {

        TryAction = tryAction;
        CatchAction = catchAction;
        FinallyAction = finallyAction;

        _hasException = false;
        _hasTryException = false;
        _hasCatchException = false;
        _hasFinnallyException = false;
        Exceptions = new Dictionary<string, Exception>();
    }


    private void Invoke(Action action, ref Boolean isError, ProcessType processType)
    {
        try
        {
            action.Invoke();
        }
        catch (Exception exception)
        {
            _hasException = true;
            isError = true;
            Exceptions.Add(processType.ToString(), exception);
        }
    }

    private void InvokeTryAction()
    {
        if (TryAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasTryException, ProcessType.Try);
    }

    private void InvokeCatchAction()
    {
        if (CatchAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
    }

    private void InvokeFinallyAction()
    {
        if (FinallyAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
    }

    public void InvokeActions()
    {
        InvokeTryAction();
        if (HasTryException)
        {
            InvokeCatchAction();
        }
        InvokeFinallyAction();

        if (HasException)
        {
            throw this;
        }
    }
}

そして、このように使用されます

try
{
    ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
    handler.InvokeActions();
}
catch (Exception exception)
{
    var processError = exception as ProcessHandler;
    /*this exception contains all exceptions*/
    throw new Exception("Error to Process Actions", exception);
}

ただし、パラメーターと戻り値の型を使用する場合は、別の話です

于 2013-11-03T17:48:44.990 に答える
2

例外は伝播し、より高いレベルで処理する必要があります。例外が上位レベルで処理されない場合、アプリケーションはクラッシュします。"finally" ブロックの実行は、例外がスローされた時点で停止します。

例外があるかどうかに関係なく、「最終的に」ブロックの実行が保証されます。

  1. try ブロックで例外が発生した後に "finally" ブロックが実行されている場合、

  2. その例外が処理されない場合

  3. そして、finally ブロックが例外をスローした場合

その後、try ブロックで発生した元の例外は失われます。

public class Exception
{
    public static void Main()
    {
        try
        {
            SomeMethod();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static void SomeMethod()
    {
        try
        {
            // This exception will be lost
            throw new Exception("Exception in try block");
        }
        finally
        {
            throw new Exception("Exception in finally block");
        }
    }
} 

詳細については素晴らしい記事

于 2016-05-20T03:55:51.280 に答える
1
public void MyMethod()
{
   try
   {
   }
   catch{}
   finally
   {
      CodeA
   }
   CodeB
}

CodeAとCodeBによってスローされた例外の処理方法は同じです。

ブロックでスローfinallyされた例外には特別なことは何もありません。コードBによってスローされた例外として扱います。

于 2010-05-26T08:28:05.983 に答える
-2

例外をスローします;)他のcatch句でその例外をキャッチできます。

于 2010-05-26T08:26:21.367 に答える