以下是代码示例:
Try
Throw New FirstException()
Finally
Throw New SecondException()
End Try
我发现它只会抛出SecondException而FirstException就会消失。
我认为FirstException将在SecondException的InnerException属性中,但它似乎不是。
我没有阻止任何事情,因为我真的不需要出现FirstException,我只是对这种行为很感兴趣。
有没有办法知道SecondException在第一次被抛出时 在上层抓住这一切?
如果第一个异常真的被第二个异常覆盖,那么它是什么 原因?
是否会以其他语言发生?这是合乎逻辑的吗?
答案 0 :(得分:2)
我猜这个问题的主要解释是,你永远不会抓住你的第一个例外并将其传递给链。如果您遇到类似上述情况,您可能会在返回原始调用者的路上抛出几个异常,那么您必须在抛出它们时捕获它们(并在创建下一个时将它们包含为内部异常):< / p>
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
抛出和捕捉异常是很昂贵的,所以最好不要扔掉,直到你准备好返回并且只是记录下去。
答案 1 :(得分:1)
.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
上面的模块以一对辅助模块开始,这些模块应该在他们自己的“异常助手”模块中。 ExceptionTest方法显示可能在Try
和Finally
块中引发异常的代码模式。 ExceptionTest2方法调用ExceptionTest并报告从中返回的异常。 ExceptionDemo调用ExceptionTest2的方式会导致Try
和Finally
块的不同组合中出现异常。
如图所示,如果在清理期间发生异常,则该异常将返回给调用者,原始异常是其Data
字典中的项。另一种模式是捕获清理时发生的异常并将其包含在原始异常的数据中(这将是未被捕获的)。我的一般倾向是,在许多情况下传播清理期间发生的异常可能更好,因为任何计划处理原始异常的代码都可能期望清理成功;如果无法满足这样的期望,那么逃避的例外应该不是调用者所期望的那样。还要注意,后一种方法需要稍微不同的方法来向原始异常添加信息,因为嵌套Try
块中抛出的异常可能需要保存有关嵌套{{{{1}中抛出的多个异常的信息。 1}}阻止。