Exception Handling


When designing methods, a defensive approach should always be taken. This means that you have to assume that your methods will at some point encounter a problem that forces the method to terminate prematurely. As a designer or developer of a class, you are bound to handle these exceptions in a graceful manner. This is because exception-handling (or the lack of it) forms as much of a part in the semantic contract between a class and its client as any other design consideration. Exception handling means either dealing with the exception internally, and taking an appropriate course of action, or catching the exception and throwing a more meaningful exception back to the client code. If our methods lack exception handling, then any exceptions that occur in the course of their execution will be passed on to the calling code anyway. The decision not to handle an exception delegates the obligation to handle the exception should it arise to the code that called our method.

Failure to anticipate the possibility of exceptions being thrown in your methods results in extra work when developing the client code. Without exception handling in our code, client code itself will have to anticipate and second-guess what has gone wrong when one of your methods is invoked. This breaks the first law of object-oriented programming – encapsulation. Only documented exceptions should ever be thrown by your methods, so that client code always knows what to expect.

As an example, consider a method that stores a customer's details. It may do this by performing an update on a database. A number of things could go wrong, for example the database might be unavailable, or a foreign key constraint violation may have occurred. If we later change the implementation of this method so that it calls a web service, we might encounter a different class of problems; network failures, or XML parsing errors. If we passed these on to client code written with the assumption that it needed to handle only database exceptions, it would likely fail to handle these new exceptions. The upshot is, allowing exceptions to permeate up from our methods exposes the inner workings of our classes, and breaches the purpose of encapsulation. If something goes wrong, the underlying cause of the exception should generally be hidden from the calling program. In this example, all the client code needs to know is that the customer details have not been stored. It can then take appropriate action to deal with this situation. An application without exception handling usually results in meaningless error messages appearing on the user's screen or worse still, the application will just crash for no apparent reason.

What Is an Exception?

An exception is thrown when a method encounters an unanticipated problem or processing error, which causes the method processing to terminate prematurely. An example of this would be a ‘file not found’, or ‘divide by zero’ exception. Unlike some programming languages that generate meaningless error numbers, the .NET Common Language Runtime throws an exception, which is a special type of object.

An exception is an object that ultimately derives from System.Exception, which is the base class for all exception classes and provides the core contract supported by all exception classes. Although System.Exception is the super type of exceptions, there are two main sub-classes of exceptions – System.SystemException, from which all .NET Framework Class Library exceptions are derived and System.ApplicationException, from which all user-defined application-specific exceptions are derived.

System.Exception exposes a number of useful read-only properties that are common to all exception classes. These properties can be examined and reported on to enhance the debugging process:

  • HelpLink:
    The HelpLink property sets or gets a URN or URL that links to an appropriate help file. This is useful in applications with user interfaces that might need to display meaningful additional information to the user.

  • InnerException:
    This property can be used to create nested exceptions. This is used when the original exception is handled by throwing a different exception class. The original exception can be embedded in InnerException to create a nested exception.

  • Message:
    The Message property is used to embed a meaningful error message usually describing the cause of the exception.

  • Source:
    The Source property holds the name of the object or application that caused the exception. If this property is not set, the name of the offending assembly is used instead.

  • StackTrace:
    This property returns a string representation of the call stack. This can be used to track the sequence of method calls leading up to the exception.

  • TargetSite: This property contains the
    System.Reflection.MethodBase of the offending exception when the StackTrace property is not a null reference.

These properties form the baseline contract for all exception classes, but most specialized exceptions extend this list of properties to include information specific to that exception class. For instance, the System.ArgumentException class also includes a ParamName property, which is used to indicate the name of the offending parameter when an invalid argument is passed to a method.

When an exception is thrown, execution of the method terminates prematurely. The method will not return a value back to the client code and any arguments passed by reference may or may not have been changed by the method. The source of the exception will immediately attempt to find an exception handler. An appropriate exception handler is found by matching the type of the exception against an exception handler for that type.

TryCatchFinally

try, catch, and finally statement blocks form the basis of structured exception handling. This is achieved by organizing a method into:

  • The code that might throw an exception (the try block)

  • Code to handle the exception (the catch block)

  • Code to perform any cleanup and releasing of system resources (the finally block)

A try block is used to isolate a block of code to be tested for an exception. The code that is tested for an exception is wrapped within a try {} statement. try blocks must always be followed by either or both of a catch and finally block.

Catch blocks are used to catch specific exception types. Although catch blocks are not mandatory, at least one catch block usually follows a try block. To catch different types of exceptions, more than one catch block can be used for different types of exception.

If a finally block exists, it is always executed regardless of whether an exception has been thrown and handled. finally blocks are not mandatory unless there is no catch block. However, because finally blocks are always executed following try and catch blocks, they should be used to perform the housekeeping necessary for graceful exception handling. This includes the release of application resources such as database connections and so forth. finally blocks are extremely useful because they are always guaranteed to execute even after an exception has been thrown. It is for this reason that finally blocks should almost always be used. Here are some guidelines for using try, catch, and finally blocks:

  • Only use try blocks around the code that you want to catch exceptions for.

  • Catch only those exceptions that the specific class and method is responsible for. Leave other exceptions to propagate back to the client code.

  • At the root of your application (usually a user interface), include a general exception handler that catches System.Exception; this can be used to handle all exceptions that propagate from the lower tiers of your application.

  • Catch exceptions starting with the most specific exceptions first then move on to general exception handlers. Catch blocks have precedence based on their order and the correct exception handler will be ignored if it is below a general exception handler.

  • In an application divided into several logical and physical tiers, try not to let all exceptions propagate from the lower tiers to the user interface. The performance overhead of retreating back up a stack trace for an appropriate exception handler can be excessive in an environment like this. The problem can also be compounded if finally blocks of code are used.

  • Use finally blocks around code that must be executed regardless of exceptions. This should include code to release system resources.

Throwing Your Own Exceptions

Exceptions can be thrown in your own code by using the throw statement. This should be done when the processing in the method encounters a terminal issue that cannot be ignored, such as an invalid argument being passed to a method:

    throw (new System.ArgumentException("Error Message",                                        "Parameter Name")); 

It is customary to specify the details of the exception in one of the exception class constructors when throwing a new exception. The System.ArgumentException permits the specification of the error message and the name of the offending parameter. These details are then made available through the properties of the System.ArgumentException class. These were discussed earlier in this section, but you can see from this example that a ParamName property has been added to the System.ArgumentException class to provide additional information to the caller.

If the calling code provides an invalid argument, it's a clear cut case for throwing an exception. The calling code is responsible for ensuring that it passes in valid data, and if it fails to do so, your method can't be expected to complete normally. Throw the exception, and you immediately pass responsibility back to the calling code.

When throwing exceptions from your classes, consider whether the exception makes sense to the calling code. If the calling code thinks it's calling a simple method that adds two numbers together, there's no point throwing a FileNotFoundException because your code logs all of its additions, and the log file is missing. The calling code won't anticipate that exception and won't be able to do anything about it. Consider catching the FileNotFoundException in your code, and then throwing a different exception that makes sense to the caller.

System.ArgumentException is built into the .NET Framework Class Library, but you can also create your own exception classes. Creating your own exception class is possible by creating a class that derives from an existing exception class. Since existing exception classes are either implicitly or explicitly derived from System.Exception, your own exception classes will be derived form it too. This means that the minimum set of properties provided by System.Exception must be supported in your exception class; however, some of these properties can be overridden and new properties can be added to specialize your exception class.

Depending on the kind of error your exception class represents, it is usually best to derive your own exceptions from System.ApplicationException. Exceptions can also be based on existing .NET Framework exceptions, but doing so means that your own exceptions may become indistinguishable if your catch blocks are not explicit enough or are in the wrong order.

To create your own exception classes follow these steps:

  • Create a class that inherits from an existing exception type. System.ApplicationException is usually the best choice.

  • Your exception should support three common constructors found in all exception classes. These constructors permit the setting of the Message and InnerException properties, the Message property on its own, and a default constructor that sets no properties. If you are deriving from an exception class other than System.ApplicationException you may also need to provide additional constructors.

  • From each of the three constructors, call the equivalent base-class constructor passing the Message and/or InnerException arguments, or the default constructor.

  • Include any additional properties that add value to your exception type. These properties should be read-only because their values can only be set through a constructor.

  • Include any other additional constructors that permit the additional property values to be set.

  • Make your exception class serializable by specifying the [Serializable] attribute. This will allow your class to cross AppDomains and CPU process boundaries when thrown.

The following is an example of an exception class for a bank account. The code file is called InsufficientFundsException.cs, and it has to be compiled as a .dll file. The exception can be thrown when trying to withdraw money when there are insufficient funds in the account:

    [Serializable]    public class InsufficientFundsException : System.ApplicationException    {      private decimal balance;      public InsufficientFundsException() : base()      {      }      public InsufficientFundsException(string message) : base(message)      {      }      public InsufficientFundsException(string message,          Exception innerException) : base(message, innerException)      {      }      public InsufficientFundsException(string message,          Exception innerException, decimal balance)          : base(message, innerException)      {         this.balance = balance;      }      public InsufficientFundsException(string message, decimal balance)          : base(message)      {        this.balance = balance;      }      public decimal Balance      {        get { return this.balance; }      }    } 

Aside from following the necessary steps required to create your own exception classes, there are a number of best practices to follow. When creating your own exception classes, follow these guidelines:

  • Always suffix your exception classes with the word Exception, as in this example, InsufficientFundsException. This will help distinguish the exception class from other classes.

  • Create your exception classes within descriptive namespaces that include the name of your organization and the name of the application. This permits custom exception classes to be fully qualified where necessary, which can help to distinguish between built-in .NET Framework Class Library exception classes, and your own application exception classes. This is also a good way of grouping exception classes by functional areas within your application.

  • Consider carefully which existing exception base class you will base your exception class on. All exception classes either implicitly or explicitly derive from System.Exception, which is the base class. The best option is to explicitly derive from System.ApplicationException, unless you need to specialize an existing exception type.

  • Custom exception classes can override some of their base-class members such as the Message property, and they can also extend the base class by providing additional properties and corresponding constructors. In our example, we include the current account Balance as a property; this adds value to the InsufficientFundsException type by returning an additional reason for the exception being thrown.

  • When catching an exception and throwing a different exception, the original exception should be nested within the InnerException property, which returns a reference to the original exception type. This technique is used to provide additional detail to the client code should it be required.

  • Don't go overboard creating exception classes for every exception you can think of. Consider creating generalized exceptions for functional areas of the application and using meaningful error messages instead. A good idea is to start by having an exception type for each logical tier of your application.

  • Try not to create elaborate hierarchies of exceptions. Try to stick to two or three levels of inheritance at the most. This will simplify your catch blocks and reduce the possibility of getting your catch blocks in the wrong order.

  • For exception classes that propagate back to the user interface, assign the HelpLink property that can be used to hyperlink to an HTML help file providing further assistance.




C# Class Design Handbook(c) Coding Effective Classes
C# Class Design Handbook: Coding Effective Classes
ISBN: 1590592573
EAN: 2147483647
Year: N/A
Pages: 90

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net