2012-09-28 37 views
4

下面的代码示例:双人间异常抛出finally块

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

我想通了,它只是抛出SecondException并FirstException刚刚消失。

我以为FirstException会在SecondException的InnerException属性内,但它看起来不是。

我没有被封锁,因为我不需要FirstException来显示,我只是对这种行为很感兴趣。

  • 有没有办法知道SecondException没有得到第一时抛出 在上层捕捉这一切?

  • 如果第一个异常真的被第二个覆盖,那么 原因是什么?

  • 这是否发生在其他语言?它合乎逻辑吗?

+2

http://stackoverflow.com/questions/1482395/what-happens-if-both-catch-and-finally-blocks-throw-exception –

+0

回答了我的第一个问题,谢谢。我仍然不知道为什么它会以这种方式发生,这是一种常见且众所周知的方式来处理多个投掷,并且没有真正的解释吗? –

回答

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 

抛出和捕获异常是昂贵的,所以它可能是最好不要扔,直到你”准备好返回并且只是登录。