【问题标题】:Double exception throwing in a try / finally block在 try / finally 块中抛出双重异常
【发布时间】:2012-09-20 10:42:41
【问题描述】:

这是代码示例:

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

我发现它只会抛出 SecondException 而 FirstException 就消失了。

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

我没有阻止任何事情,因为我真的不需要出现 FirstException,我只是对这种行为很感兴趣。

  • 有没有办法知道 SecondException 在什么时候首先被抛出 在上层抓住一切?

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

  • 其他语言都会出现这种情况吗?合乎逻辑吗?

【问题讨论】:

  • 回答了我的第一个问题,谢谢。我仍然不知道为什么会发生这种情况,这是一种常见且众所周知的处理多次抛出的方法并且没有真正的解释吗?

标签: vb.net exception try-finally


【解决方案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 方法显示了可能在TryFinally 块中引发异常的代码模式。 ExceptionTest2 方法调用 ExceptionTest 并报告从它返回的异常。 ExceptionDemo 调用 ExceptionTest2 的方式是在 TryFinally 块的不同组合中引发异常。

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

【讨论】:

    【解决方案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
    

    抛出和捕获异常的成本很高,因此最好不要抛出异常,直到您准备好返回并沿途记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-23
      • 2010-10-03
      • 1970-01-01
      • 2017-10-03
      • 2011-03-28
      • 2018-02-20
      • 2011-03-18
      相关资源
      最近更新 更多