Thursday, May 29, 2008

Best Practices - Exception Handling

The developer develops the code by sitting day and night but at the end if the code fails because of some exception which he/she failed to handle during the development then everything goes for a toss. I thought of sharing the best practices on Exception Handling which can help you all to write good code.

Only throw exceptions in exceptional situations

Do not throw exceptions in situations that are normal or expected (e.g. end-of-file). Use return values or status enumerations instead. In general, try to design classes that do not throw exceptions in the normal flow of control. However, do throw exceptions that a user is not allowed to catch when a situation occurs that may indicate a design error in the way your class is used.

Do not throw exceptions from inside destructors

When you call an exception from inside a destructor, the CLR will stop executing the destructor, and pass the exception to the base class destructor (if any). If there is no base class, then the destructor is discarded.

Re-throwing of exceptions

Exceptions should be re-thrown in one of two situations:

  1. You want to specialize the exception and possibly provide further information.
  2. You need to perform some error correction due to the exception but are then also required to allow the exception to propagate back to the caller

To specialize the exception, you must set the innerException property of the new exception you are throwing like so: -

try
{
//code…
}
catch(SOAPException e)
{
Throw new RemapUIException("Unable to communicate with SSF", e)
}

This will ensure the stack trace of where the original exception occurred is preserved. To perform error correction code and then propagate the exception higher up the call stack, issue the throw statement on its own. Never re-throw the same exception as this will create a new exception stack trace and destroy the original stack info:-

WRONG:-

try
{
//code…
}
catch([ExceptionType] e)
{
//perform catch and error handling code..
throw e;
}

CORRECT:-

try
{
//code…
}
catch([ExceptionType] e)
{
//perform catch and error handling code..
throw;
}

List the explicit exceptions a method or property can throw.

Describe the recoverable exceptions using the <exception> tag. Explicit exceptions are the ones that a method or property explicitly throws from its implementation and which users are allowed to catch. Exceptions thrown by .NET framework classes and methods used by this implementation do not have to be listed here.

Always log that an exception is thrown

Logging ensures that if the caller catches your exception and discards it, traces of this exception can be recovered at a later stage.

Allow callers to prevent exceptions by providing a method or property that returns the objects state

For example, consider a communication layer that will throw an InvalidOperationException when an attempt is made to call Send() when no connection is available. To allow preventing such a situation, provide a property such as Connected to allow the caller to determine if a connection is available before attempting an operation.

Use standard exceptions

The .NET framework already provides a set of common exceptions. The table below summarizes the most common exceptions that are available for applications.






























Exception Condition
IndexOutOfRangeException Indexing an array or indexable collection outside its valid range.
InvalidOperationException An action is performed which is not valid considering the object's current state.
NotSupportedException An action is performed which is may be valid in the future, but is not supported.
ArgumentException An incorrect argument is supplied.
ArgumentNullException An null reference is supplied as a method's parameter that does not allow null.
ArgumentOutOfRangeException An argument is not within the required range.

Throw informational exceptions

When you instantiate a new exception, set its Message property to a descriptive message that will help the caller to diagnose the problem. For example, if an argument was incorrect, indicate which argument was the cause of the problem. Also mention the name (if available) of the object involved.


Also, if you design a new exception class, note that it is possible to add custom properties that can provide additional details to the caller.

Throw the most specific exception possible

Do not throw a generic exception if a more specific one is available.

Only catch the exceptions explicitly mentioned in the documentation

Moreover, do not catch the base class Exception or ApplicationException or System.SystemException. Exceptions of those classes generally mean that a non-recoverable problem has occurred.

Exception:

On system-level or in a thread-routine, it is allowed to catch the Exception class directly, but only when approval by the Senior Designer has been obtained.

Derive custom exceptions from ApplicationException

All exceptions derived from SystemException are reserved for usage by the CLR only.

Provide common constructors for custom exceptions

It is advised to provide the three common constructors that all standard exceptions provide as well.

These include:

XxxException()
XxxException(string message)
XxxException(string message, Exception innerException)

Avoid side-effects when throwing recoverable exceptions

When you throw a recoverable exception, make sure that the object involved stays in a usable and predictable state. With usable it is meant that the caller can catch the exception, take any necessary actions, and continue to use the object again. With predictable is meant that the caller can make logical assumptions on the state of the object.

For instance, if during the process of adding a new item to a list, an exception is raised, then the caller may safely assume that the item has not been added, and another attempt to re-add it is possible.

Do not throw an exception from inside an exception constructor

Throwing an exception from inside an exception's constructor will stop the construction of the exception being built, and hence, preventing the exception from getting thrown. The other exception is thrown, but this can be confusing to the user of the class or method concerned.

Custom Exceptions

To avoid ambiguity, all custom exceptions should descend from a new base exception and create a new set of exception hierarchies specific to the application or Framework. This will then allow all custom application/framework exceptions to be caught by one catch if required (and totally removes the requirement of catching ApplicationException in such situations).

Performing the above provides the following advantages:-

  • No custom exceptions directly fall under ApplicationException. This is important as certain reflection exceptions 'appear' under the ApplicationException class tree so these could inadvertently be caught if ApplicationException is used to 'catch' any 'application error' that has occurred.
  • Provides an easy mechanism to separate out different custom exception hierarchies, based on each application/framework.
  • Provides an easy way to catch 'related' exceptions without having to determine the except type.

Article courtesy: Ashok Kumara Purlehalli from Microsoft

Some other best practices articles:

  1. Java - Best practices for Exception Handling
  2. EJB - Best practices in EJB Exception Handling
  3. Exception Handling Best Practices in .NET

Technorati Tags: ,,,