4

コード例は次のとおりです。

Try
    Throw New FirstException()
Finally
    Throw New SecondException()
End Try

私はそれがSecondExceptionをスローするだけで、FirstExceptionが消えることを理解しました。

FirstExceptionはSecondExceptionのInnerExceptionプロパティ内にあると思いましたが、そうではないようです。

FirstExceptionを表示する必要がないため、何もブロックされていません。この動作に興味を持っています。

  • すべてを上位レベルでキャッチしたときにSecondExceptionが最初にスローされたことを知る方法はありますか?

  • 最初の例外が実際に2番目の例外によってオーバーライドされる場合、その理由は何ですか?

  • それは他のすべての言語で起こりますか?それは論理的ですか?

4

2 に答える 2

2

これがこのように機能する理由の主な説明は、最初の例外をキャッチしてチェーンに沿って渡すことは決してないということだと思います。上記のような状況で、元の呼び出し元に戻る途中でいくつかの例外がスローされる可能性がある場合は、スローされたときにそれらをキャッチする必要があります(次の例外を作成するときに内部例外としてそれらを含める必要があります):

Dim ex1 As Exception = Nothing
Try
    Throw New Exception("first exception")
Catch ex As Exception
    ex1 = ex
Finally
    Throw New Exception("second exception", ex1)
End Try

または、おそらくもっと良いです-すべての例外が理解されるまでスローしないでください:

Dim ex1 As Exception = Nothing
Try
    ex1 = New Exception("first exception")
Finally
    Throw New Exception("second exception", ex1)
End Try

例外のスローとキャッチにはコストがかかるため、戻る準備ができて途中でログに記録するまでスローしないのがおそらく最善です。

于 2012-09-28T14:51:05.287 に答える
1

.netでの例外処理の制限の1つは、Finallyブロック内のコードが、ブロック内のコードを終了させた例外があればそれを知るための優れた方法がないことですTry。また、finallyのコードの通常の方法もありません。例外をスローする可能性のあるコードで利用できるようにするためのそのような情報を持っているブロック。

vb.netでは、少し醜いように見えますが、かなりうまく機能する方法で物事をこじ開けることができます。

Module ExceptionDemo
    Function CopySecondArgToFirstAndReturnFalse(Of T)(ByRef dest As T, src As T) As Boolean
        dest = src
        Return False
    End Function
    Function AnnotateExceptionAndReturnFalse(ex As Exception, TryBlockException As Exception) As Boolean
        If ex Is Nothing Then Return False ' Should never occur
        If TryBlockException Is Nothing Then Return False ' No annotation is required
        ex.Data("TryBlockException") = TryBlockException
        Return False
    End Function

    Sub ExceptionTest(MainAction As Action, CleanupAction As Action)
        Dim TryBlockException As Exception = Nothing
        Try
            MainAction()
        Catch ex As Exception When CopySecondArgToFirstAndReturnFalse(TryBlockException, ex)
            ' This block never executes, but above grabs a ref to any exception that occurs
        Finally
            Try
                CleanupAction()
            Catch ex As Exception When AnnotateExceptionAndReturnFalse(ex, TryBlockException)
                ' This block never executes, but above performs necessary annotations
            End Try
        End Try
    End Sub

    Sub ExceptionTest2(Message As String, MainAction As Action, CleanupAction As Action)
        Debug.Print("Exception test: {0}", Message)
        Try
            ExceptionTest(MainAction, CleanupAction)
        Catch ex As Exception
            Dim TryBlockException As Exception = Nothing
            Debug.Print("Exception occurred:{0}", ex.ToString)
            If ex.Data.Contains("TryBlockException") Then TryBlockException = TryCast(ex.Data("TryBlockException"), Exception)
            If TryBlockException IsNot Nothing Then Debug.Print("TryBlockException was:{0}", TryBlockException.ToString)
        End Try
        Debug.Print("End test: {0}", Message)
    End Sub
    Sub ExceptionDemo()
        Dim SuccessfulAction As Action = Sub()
                                             Debug.Print("Successful action")
                                         End Sub
        Dim SuccessfulCleanup As Action = Sub()
                                              Debug.Print("Cleanup is successful")
                                          End Sub
        Dim ThrowingAction As Action = Sub()
                                           Debug.Print("Throwing in action")
                                           Throw New InvalidOperationException("Can't make two plus two equal seven")
                                       End Sub
        Dim ThrowingCleanup As Action = Sub()
                                            Debug.Print("Throwing in cleanup")
                                            Throw New ArgumentException("That's not an argument--that's just contradiction")
                                        End Sub
        ExceptionTest2("Non-exception case", SuccessfulAction, SuccessfulCleanup)
        ExceptionTest2("Exception in main; none in cleanup", ThrowingAction, SuccessfulCleanup)
        ExceptionTest2("Exception in cleanup only", SuccessfulAction, ThrowingCleanup)
        ExceptionTest2("Exception in main and cleanup", ThrowingAction, ThrowingCleanup)
    End Sub
End Module

上記のモジュールは、おそらく独自の「例外ヘルパー」モジュールに含まれているはずのいくつかのヘルパーモジュールから始まります。TryExceptionTestメソッドは、とFinallyブロックの両方で例外をスローする可能性のあるコードのパターンを示します。ExceptionTest2メソッドはExceptionTestを呼び出し、例外が発生した場合にそれを報告します。TryExceptionDemoは、とFinallyブロックのさまざまな組み合わせで例外を発生させるような方法でExceptionTest2を呼び出します。

示されているように、クリーンアップ中に例外が発生した場合、その例外は呼び出し元に返され、元の例外はその中のアイテムです。Data辞書。別のパターンは、クリーンアップ時に発生する例外をキャッチし、それを元の例外のデータに含めることです(これはキャッチされないままになります)。私の一般的な傾向は、多くの場合、クリーンアップ中に発生する例外を伝播する方が良いということです。元の例外を処理することを計画していたコードは、おそらくクリーンアップが成功したことを期待するからです。そのような期待に応えられない場合、エスケープの例外は、おそらく呼び出し元が期待していたものではないはずです。Tryネストされたブロックでスローされた例外は、ネストされたブロックでスローされた複数の例外に関する情報を保持する必要がある場合があるため、後者のアプローチでは、元の例外に情報を追加する方法が少し異なることにも注意してくださいFinally

于 2012-09-28T15:29:43.393 に答える