October 2, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Don't Commit Errorcide, Part I

  • November 26, 2003
  • By Paul Kimmel
  • Send Email »
  • More Articles »

Return Statements

Another mistake that seems to be commonly made is declaring a temporary variable and placing the return statement after the Try Finally block. Suppose we wanted to read something from our text file and return that. Listing 3 shows this example with the mistakenly placed Return statement.

Listing 3: Don't use temporary variables and an extra-try block return statement.

Public Function GetText() As String
  Dim MyTextFile As TextReader = File.OpenText("some.txt")
  Dim Temp As String
  Try
    Temp = MyTextFile.ReadLine()
  Finally
    MyTextFile.Close()
  End Try
  Return Temp
End Function

The preceding example is grammatically correct. Assuming some.txt exists and contains some code, GetText will read and return that text. Applying subjective rules relative to innovations in structured exception handling and Refactoring, we can and should tighten up the code as demonstrated in Listing 4.

Listing 4: A refactored, tighter implementation of GetText.

Public Function GetText() As String
  Dim MyTextFile As TextReader = File.OpenText("some.txt")
  Try
    Return MyTextFile.ReadLine()
  Finally
    MyTextFile.Close()
  End Try
End Function

Temporary variables usually clutter up code without adding any significant value. Martin Fowler's book Refactoring: Improving the Design of Existing Code provides reasonable arguments against using spurious random temporary variables, and the Finally block does not require it. The code looks as if it might exit at the Return statement (in bold font), but remember the Finally block is always run. Thus, even with an internal statement, we can easily step through the code and determine that the Return statement executes, followed by the code in the Finally block. Beneficially, our code is simplified by about a third and appears to be less cluttered. Functionally, the code in Listing 4 is equivalent to the code in Listing 3.

Catching an Exception

When do we catch an exception? The answer is a bit of a slippery slope. We should catch an exception if we can handle the error at the locus of the catch block. And, we can catch an exception if we want to log the exception and propagate it, permit silent errors to occur, catch the exception, degrade, and shut down, or catch an exception, wrap it in a new exception, and propagate the new exception. Technically, you can catch an exception for any reason you want, but it is a waste of code to catch an exception that you can or should do nothing about.

The following listings demonstrate each of the techniques mentioned in the preceding paragraph. The listing headers describe the technique demonstrated.

Listing 5: Handling an exception.

Public Sub HandlingAnException()
  Try
    Process.Start("http://www.softconcepts.com")
  Catch
    Console.WriteLine("Can't load Web page")
  End Try
End Sub

In the example, we catch an exception that might occur when we try to browse to a Web site. Handling in this context might be as simple as telling the user that the Web page cannot be loaded. In the example, this permits the application to continue and provides the user with enough information to manually resolve the problem. If we can automatically resolve the problem, that is an even better solution.

Listing 6: Catching an exception, degrading, and shutting down.

Public Sub HandleAndShutdown()
  Try
    Console.WriteLine(GetText())
  Catch
    Shutdown("There is a problem with the file.")
  End Try
End Sub

Private Sub Shutdown(ByVal Message As String)
  Console.WriteLine(Message)
  Console.WriteLine("press enter to exit")
  Console.ReadLine()
  End
End Sub

Listing 6 tries to use the GetText method to read the text file. If something goes wrong, we tell the user and shut down the application. The End statement in the sample terminates the console application immediately. (Use Application.Exit instead of End for a Windows application.) Shutting down gracefully is another example of a contextually subjective concept. The application suggested by Listing 6 is a simple text-reading solution. If the text file to read doesn't exist, the user needs to intercede and try again. In a real world application, degrading and shutting down will likely be more involved but the result is the same: Instead of a shocking blue screen of death, the user is pleasantly prepared for the shutdown and has a chance to resolve the problem.

Listing 7: Logging an exception and propagating it.

Imports System.Diagnostics

Public Sub LogAndPropagate()
  Try
    Process.Start("http://www.softconcepts.com")
  Catch
    EventLog.WriteEntry("codeguru.com", "Can't load web page.")
    Throw
  End Try
End Sub

In Listing 7, the programmer decided that the error should be logged but further handling cannot be accomplished in the current context. To permit an outer context—the caller—an opportunity to resolve the problem, the code re-throws the original exception after logging a record of the error in the EventLog.

Listing 8: Silent exception.

Public Sub SilentException()
  Try
    File.Delete("b:\google.txt")
  Catch
  End Try
End Sub

Listing 8 implies that the implementer does not care whether the file is deleted or not. The algorithm that produced such a procedure might read: Delete the file b:\google.txt if it's not too much trouble; it isn't that important, but don't fail.

Listing 9 demonstrates how to create a custom exception class, catch, wrap, and throw a new exception. The class BetterException represents a domain-specific class of error. This might be a class that is meaningful to your application. The first bold statement containing Throw New simulates an error occurring in the Try block of a specific method. The catch block demonstrates how to catch any exception that might occur and wrap it in a new, domain-specific exception, throwing that new exception.

Listing 9: Wrapping an exception and propagating a new exception.

Public Class BetterException
  Inherits System.Exception

  Public Sub New(ByVal Message As String)
    MyBase.New(Message)
  End Sub

  Public Sub New(ByVal Message As String, _
                 ByVal InnerException As Exception)
    MyBase.New(Message, InnerException)
  End Sub
End Class

Public Sub WrapAndPropagate()
  Try
    Throw New Exception("Some error")
  Catch ex As Exception
    Throw New BetterException("This is more meaningful", ex)
  End Try
End Sub

Consider a contrived scenario. Your application performs currency conversions. You attempt to open a file containing the daily currency conversion rates, but the conversion file cannot be found. It is likely that a FileNotFoundException will occur. At the location you attempt to read such a file, this is a valid exception. However, in the domain of currency conversions, it may be more meaningful to wrap the FileNotFoundException in a CurrencyConversionException as the inability to convert the currency conveys more meaning to end users. The outer CurrencyConversionException lets the user know the request to convert between currencies failed, and the inner FileNotFoundException helps a diagnostician resolve the problem. (See Creating Custom Exceptions, in Part II of this article for more information on defining custom exceptions.)

Summary

In Part I of this article on preventing errorcide, we talked about the grammar of structured exception handling, including how to use Try, Catch, and Finally blocks to catch errors and protect resources. We also talked about some of the different error handling techniques we might employ, including logging, wrapping, and propagating exceptions. These fundamental exception handling capabilities are essential to writing robust, efficacious, software.

In Part II, we will examine more closely how to catch multiple exceptions, re-throw exceptions, when and how to create custom exceptions, and how to eliminate excessive exception handling that is at the root of errorcide.

Happy Thanksgiving!

About the Author

Paul Kimmel has written several books on Visual Basic, including the recently released Visual Basic .NET Power Coding from Addison Wesley. Pick up your copy at www.amazon.com. Paul is also available for short- and long-term consulting and .NET training. You may contact him at pkimmel@softconcepts.com.

# # #





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel