Double exception throws at try / finally block

Here is a sample code:

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

I realized that it only throws a SecondException and the FirstException just disappears.

I thought that FirstException would be inside the InnerException property for SecondException, but that doesn't seem to be the case.

I am not blocked by anything, since I really do not need FirstException to be displayed, I am simply intrigued by this behavior.

  • Is there a way to find out that SecondException was thrown first when catching all this at the top level?

  • If the first exception is indeed overridden by the second, what is the reason?

  • Does this happen in any other language? Is that logical?

+4
source share
2 answers

One of the limitations of handling .net exceptions is that for the code in the Finally block there is no good way to find out which exception, if any, caused the code in the Try block to exit, and there is also a normal way for the code in the finally block that has such information to make it accessible to code that may throw an exception.

In vb.net, it allows you to clone things in a way that works very well, although it looks a bit ugly.

 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 not an argument--that 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 

The module above starts with a few helper modules, which probably should be in their own Exceptional Helpers module. The ExceptionTest method shows a template for code that can throw an exception in the Try and Finally block. The ExceptionTest2 method calls ExceptionTest and reports which exception if it returns. ExceptionDemo raises ExceptionTest2 in such a way as to throw exceptions in different combinations of Try and Finally blocks.

As shown, if an exception occurs during cleaning, this exception will be returned to the caller, with the original exception being an element in its Data dictionary. An alternative pattern would be to catch the exception that occurs during the cleanup and include it in the data of the original exception (which would remain unmapped). My general tendency is that in many cases it is better to throw an exception that occurs during the cleanup, since any code that planned to deal with the original exception probably expects the cleanup to succeed; if such a wait cannot be fulfilled, the exception that escapes should probably not be the one that the caller expected. Note also that the latter approach will require a slightly different way of adding information to the original exception, because for the exception that is selected in the nested Try block, you may need to store information about several exceptions that were selected in the nested blocks Finally .

+1
source

My guess is that the main explanation for why this works this way is that you will never catch your first exception and don't pass it along the chain. If you have a situation like the one above where you can throw a few exceptions on the way back to the original caller, then you will either have to catch them as they throw them (and include them as an internal exception when creating the following):

 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 

Or, probably, it’s better - just don’t give up until you find all the exceptions:

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

Throwing and catching exceptions is expensive, so it is probably best not to throw until you are ready to return and just hit the road.

+2
source

Source: https://habr.com/ru/post/1436829/


All Articles