.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
上記のモジュールは、おそらく独自の「例外ヘルパー」モジュールに含まれているはずのいくつかのヘルパーモジュールから始まります。Try
ExceptionTestメソッドは、とFinally
ブロックの両方で例外をスローする可能性のあるコードのパターンを示します。ExceptionTest2メソッドはExceptionTestを呼び出し、例外が発生した場合にそれを報告します。Try
ExceptionDemoは、とFinally
ブロックのさまざまな組み合わせで例外を発生させるような方法でExceptionTest2を呼び出します。
示されているように、クリーンアップ中に例外が発生した場合、その例外は呼び出し元に返され、元の例外はその中のアイテムです。Data
辞書。別のパターンは、クリーンアップ時に発生する例外をキャッチし、それを元の例外のデータに含めることです(これはキャッチされないままになります)。私の一般的な傾向は、多くの場合、クリーンアップ中に発生する例外を伝播する方が良いということです。元の例外を処理することを計画していたコードは、おそらくクリーンアップが成功したことを期待するからです。そのような期待に応えられない場合、エスケープの例外は、おそらく呼び出し元が期待していたものではないはずです。Try
ネストされたブロックでスローされた例外は、ネストされたブロックでスローされた複数の例外に関する情報を保持する必要がある場合があるため、後者のアプローチでは、元の例外に情報を追加する方法が少し異なることにも注意してくださいFinally
。