| only for RuBoard |
6.5 Retrying Code
Sometimes it is necessary to rerun code that has triggered an error. Unfortunately, there is no good way to rerun code by using structured exception handling. Example 6-9 shows one method that uses nested Try blocks, which is ideal if you need to retry code only a few times.
Example 6-9. Retrying a method
Public Shared Sub Main( )
Try
RetryMethod( )
Catch e As Exception
Try
RetryMethod( )
Catch e As Exception
'Fail
End Try
End Try
End Sub Nested Try blocks are great unless you want to retry your code more than a few times. Beyond that, reading the code becomes more difficult. If someone has to scroll the code window out to column 420 to see what you have donewell, they will laugh at you and tell people about it on the elevator.
Try blocks are re-entrant , so you can use a Goto to jump back into a Try block that already executed. Example 6-10 demonstrates this technique.
Example 6-10. Retrying code after a failure
Imports System
Public Class App
Public Shared Sub Main( )
Dim attempts As Integer = 0
Dim maxRetries As Integer = 5
Try
'Code to be retried n times
retry:
attempts += 1
Console.WriteLine("Attempt {0}", attempts.ToString( ))
'Simulate error - jumps to Catch block
Throw New Exception( )
Catch e As Exception
If attempts = maxRetries Then
GoTo done
End If
GoTo retry
done:
End Try
End Sub
End Class This code does not flow well, and it is difficult to read. It exists just in case someone out there decides to be clever. Stop trying to be clever when you code. There is a better way to retry a path of execution; you can use recursion from a Catch block and monitor how many attempts were made on the method with a private instance variable, which is shown in Example 6-11.
The Goto Is ObsoleteDon't believe the hype. Before structured error handling, the Goto did have a place in structured code. When several resources, such as database connections and file handles, were allocated, the Goto allowed a single "cleanup" location. Consider the following pseudocode: Public Sub PseudoCode( )
Dim conn As New OleDbConnection(...)
Dim fStream As FileStream = File.Open(...)
If error Then
Goto cleanup
End If
If error Then
Goto cleanup
End If
If error Then
Goto cleanup
End If
cleanup:
conn.Close( )
fstream.Close( )
End Sub This pattern allows cleanup to be performed in a single place rather than being repeated whenever an error occurs. However, this pattern is no longer necessary because with structured exception handling, cleanup code can be placed in a Finally block. |
Example 6-11. A better way to retry code
Imports System
Public Class Retry
Private attempts As Integer = 0
Private Const maxRetries As Byte = 5
Public Sub Go( )
Try
'Code to retry0
Console.WriteLine("Attempt #{0}", _
attempts.ToString( ))
'Simulate error
Throw New Exception( )
Catch e As Exception When attempts < maxRetries
attempts += 1
Go( ) 'Call method again
Catch e As Exception
'Failed so handle here
End Try
'Reset attempt counter
attempts = 0
End Sub
End Class
Public Class App
Public Shared Sub Main( )
Dim r As New Retry( )
r.Go( )
Console.ReadLine( )
End Sub
End Class Retrying code multiple times is considerably easier with the conditional handling of exceptions. Remember, exceptions are caught in the order they are received. In Example 6-11, the first exception handler's When clause provides a simple alternative to rerunning a block of code:
Try
Go( ) 'Code that you want to re-run on failure
Catch e As Exception When attempts < maxRetries
attempts += 1
Go( ) 'Call method again
Catch e As Exception
'Failed so handle here
End Try After the internal counter exceeds its limit, the condition on the first Catch block is no longer valid, and the second handler catches any exception. The example ignores the exception, but in this situation, you could probably log the error as discussed earlier in the chapter.
|
| only for RuBoard |





