Vb.net 在try/finally块中引发双重异常

Vb.net 在try/finally块中引发双重异常,vb.net,exception,try-finally,Vb.net,Exception,Try Finally,下面是代码示例: 试试看 抛出新的FirstException() 最后 抛出新的SecondException() 结束尝试 我发现它只会抛出第二个异常,而第一个异常就消失了 我原以为FirstException会在SecondException的InnerException属性中,但它似乎不是 我对任何事情都不感兴趣,因为我真的不需要出现第一个异常,我只是对这种行为很感兴趣 有没有办法知道SecondException在 在高层抓住一切 如果第一个异常确实被第二个异常覆盖,那么 原因 其

下面是代码示例:

试试看
抛出新的FirstException()
最后
抛出新的SecondException()
结束尝试
我发现它只会抛出第二个异常,而第一个异常就消失了

我原以为FirstException会在SecondException的InnerException属性中,但它似乎不是

我对任何事情都不感兴趣,因为我真的不需要出现第一个异常,我只是对这种行为很感兴趣

  • 有没有办法知道SecondException在 在高层抓住一切

  • 如果第一个异常确实被第二个异常覆盖,那么 原因

  • 其他语言都有吗?这合乎逻辑吗


我想,对于这种方式的主要解释是,您永远不会捕获第一个异常并沿链传递它。如果出现上述情况,您可能会在返回原始调用方的过程中抛出多个异常,那么您必须在抛出时捕获它们(并在创建下一个异常时将它们作为内部异常包括在内):

或者,可能更好——在解决所有异常之前不要抛出:

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

抛出和捕获异常的代价很高,因此在准备返回之前最好不要抛出异常,只需一路记录即可。

在.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
块的不同组合出现异常


如图所示,如果在清理过程中发生异常,该异常将返回给调用方,原始异常是其
数据
字典中的一项。另一种模式是捕获清理时发生的异常,并将其包含在原始异常的数据中(未捕获)。我的总体倾向是,在许多情况下,传播清理期间发生的异常可能更好,因为任何计划处理原始异常的代码都可能期望清理成功;如果不能满足这样的期望,那么转义的异常可能不是调用方期望的异常。还请注意,后一种方法将需要一种稍微不同的方法向原始异常添加信息,因为在嵌套的
Try
块中抛出的异常可能需要包含有关嵌套的
最后
块中抛出的多个异常的信息。

回答了我的第一个问题,谢谢。我仍然不知道为什么会这样,但这是一个常见的和众所周知的方式来处理多次抛出,并没有真正的解释?
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