6.5 Retrying Code

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 Obsolete

Don'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.

Use unstructured exception handling only when migrating from earlier versions of VB to the .NET environment. New projects should always use structured exception handling.

only for RuBoard