.net 中异常处理的限制之一是块中的代码没有很好的方法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 方法显示了可能在andFinally
块中引发异常的代码模式。ExceptionTest2 方法调用 ExceptionTest 并报告从它返回的异常。Try
ExceptionDemo 调用 ExceptionTest2 的方式是在和Finally
块的不同组合中引起异常。
如图所示,如果在清理过程中发生异常,该异常将返回给调用者,原始异常是其Data
字典。另一种模式是捕获清理时发生的异常并将其包含在原始异常的数据中(不会被捕获)。我的一般倾向是,在许多情况下,传播清理期间发生的异常可能会更好,因为任何计划处理原始异常的代码都可能期望清理成功;如果无法满足这样的期望,则逃逸的异常可能不应该是调用者所期望的异常。另请注意,后一种方法需要稍微不同的方法来向原始异常添加信息,因为嵌套块中引发的异常Try
可能需要保存有关嵌套Finally
块中引发的多个异常的信息。