posted on Friday, December 03, 2004 2:17 PM by anoras

Retry statement considered harmful

There is a hearty discussion going on in the .NET newsgroup on the usefulness of a try-catch-retry clause in C# and VB.NET. Since the discussion has branched in all directions, I decided to write something about it here.
Nay Myo Aung proposed the clause with the follow explanation and sample code.

“We .NET programmers could use the keyword RETRY in the any of the Catch
blocks to re-execute the code in Try block. If there's another exception
thrown, we can evaluate that exception in one of the many Catch blocks until
it reached to the ‘unknown’ exception.”

Try
    '...something
Catch e as FileNotFoundException
    ' Correct the error and then
    ReTry
Catch e2 as DirectoryNotFoundException
    ' Correct the error and then
    ReTry
Catch e3 as DriveNotFoundException
    ' Correct the error and then
    ReTry
Catch '<< catch 'unknown' error
    ' Log the error
Finally
End Try

The rescue clause in the Eiffel programming language that is very similar to what Nay Myo Aung proposes. An abridged Eiffel version of the above code can be implemented like this:

do
   -- ...something
rescue
   -- Correct the error and then
  retry
end

If an exception occurs in an Eiffel routine with a rescue clause the processing of the normal method body ceases and control is transferred to the code within the rescue clause. The code in this clause can reassign variables, sleep to cause a thread race and similar and then execute the retry instruction. The retry instruction restarts the routine from the beginning without reinitializing local entities.
Although Eiffel has this clause it is very seldom used. The Eiffel documentation points out that the clause is only used 16 times in the 2000 classes in the Eiffel libraries. The few classes that use the rescue and retry clauses are oriented toward networking and database operations. This is reasonable, because these are situations where it you can retry the operation that caused the exception after waiting. Consider the following C# example:

SqlConnection connection=new Connection();
connection.ConnectionString=myConnectionString;
for (int i=0; i<maxRetries; i++) {
    try {
        connection.Open();
    } catch (SqlException ex) {
        Thread.Sleep(5000);
    }
}

If the above code is used in a Windows Service, the reason for the SqlException being thrown when the Open operation is called could be that the SQL Server hasn’t started yet. Then it makes sense to wait five seconds and retry. Note that this can be done without a retry keyword.
Nay Myo Aung uses file operations in his example. From my point of view he uses exceptions because of laziness.
The “design guidelines for class library developers” states that all code paths that result in an exception should provide a method to check for success without throwing an exception. For example, to avoid a FileNotFoundException you can call File.Exists. This might not always be possible, but the goal is that under normal execution no exceptions should be thrown.

The code for accessing a file should therefore look like this:

if (File.Exists(filename)) {
    try {
       // Do something...
    } catch {}
}

The key argument for having a retry statement is that you’ll have to duplicate code in the catch statement to retry. If you do proper checks like checking if a file exists before trying to open it and checking if a connection is open before trying to close it, you’ll avoid this. Exceptions are just as the name says – exceptions. You should not be surprised by a FileNotFoundException. Catching exceptions instead of checking for success also has significant performance implications – the catch clause is expensive.

If you want to have a Eiffel-esque retry statement you can achive this with C#’s existing vocabulary. Just do this:

retryFromHere:
try {
     // Do something
} catch {
    // Sort things out.
   goto retryFromHere;
}

Even if it is possible, I would not recommend using the above approach. Edsger Dijkstra’s 36 year old paper, “Go To statement considered harmful” is still valid. Jumping around messes up your code and use of goto statements are rooted in bad design 99.99% of the time.

Comments