Friday, June 22, 2007

Exception Handling

C# Exception Handling

What is an Exception?
An exception is an error or failure that occurs when a program is executing. Generally, an exception describes an event that was unexpected. For example, an exception will occur if a program requests more memory than the operating system can provide. This error is known as an Out of Memory Exception.

The Exception Class Hierarchy
When an exception occurs normal processing of the code ceases immediately. An object is then generated that contains information relating to the error. This object is an instance of an Exception class. The most basic of the exception classes is System.Exception, the Exception class defined in the System namespace.

The exception classes are organised into a hierarchy with Exception at the top. Beneath the basic Exception class are two further classes, SystemException and ApplicationException, both in the System Namespace. The SystemException class has further derived subclasses, each representing a specific type of exception that can be raised in response to a system error. The ApplicationException class is a base class from which custom application exceptions may be derived.

NB: Subclass and base class are object-oriented programming terms. Object oriented programming is beyond the scope of the C# Fundamentals tutorial and will be described in a future series of articles.

System Exceptions
There are many system exceptions that may be raised, or thrown, when a problem occurs. These cause the generation of an object based on its own specialised exception class derived from SystemException. Examples include:

ArithmeticException. Thrown when an error occurs during an arithmetic operation or during casting or converting.
DivideByZeroException. Thrown when an attempt to divide a value by zero occurs. The DivideByZeroException is a more specialised version of ArithmeticException.
OverflowException. Thrown when an error occurs during an arithmetic operation or during casting or converting because the resultant value is too large or too small. The OverflowException is derived from ArithmeticException.
OutOfMemoryException. Thrown when the available memory is insufficient to continue execution of a program.
The above list gives a small sample of some of the exception classes within the hierarchy. A much more comprehensive list can be found in the System Exceptions Hierarchy page of Microsoft's MSDN web site.

Application Exceptions
Application exceptions are defined by the programmer and can be quite generic, for example WordProcessorException, or very specialised, such as LeftMarginTooSmallException. Of course, neither of these exceptions exists within the .NET framework; they must be created before use. For this reason, application exceptions will not be discussed in this article. Instead they will be described in the next article, which investigates programmatically throwing both standard and custom exceptions.

Handling Exceptions
Unhandled Exceptions
When an exception occurs the program flow for the executing method is immediately interrupted. If the exception is not handled explicitly, the method exits and the exception is propagated back to the calling function. This calling method then has the opportunity to handle the error. This process continues until the exception is handled by the program or, if never handled, it reaches the C# runtime system.

An unhandled exception that reaches the C# runtime system causes the immediate, abnormal termination of the program. This can be a problem as the exception is reported to the user as a message or dialog box containing standard information and technical details that may be misunderstood. During debugging this may be useful but in a production system it is generally considered to be unacceptable. It can also permit the user to attempt to continue running a program that, due to errors, has become unstable; note the Continue button in the error dialog box below:



The Basic Try / Catch Block
C# provides a code structure known as the try / catch block that permits the handling of exceptions. A basic try / catch block has two elements. The try section contains a series of commands to be executed, held within the brace characters { and }. The catch section contains code to execute should an exception occur during processing of the try section. The basic syntax is as follows:

try
{
// commands to execute whilst checking for exceptions
}
catch
{
// commands to execute if an exception occurs
}When using this basic exception capturing syntax, any exception that occurs causes the code in the try block to be left and the code in the catch block to be executed. The catch block code can be used for various purposes including graceful recovery from the error, logging or reporting of the details of the problem, and freeing up resources such as database connections or open files. Once the catch block has finished executing, or if no exception occurs within the try block, the program continues with the next statement after the try / catch structure.

The following example attempts to divide a value by zero causing an exception to be thrown. In this case, a simple error message is reported to the user and the calculated value is set to the highest possible integer value.

static void Main(string[] args)
{
int value = 50;
int divisor = 0;
int calculated;

try
{
calculated = value / divisor;
}
catch
{
Console.WriteLine("An error occurred during division.");
calculated = int.MaxValue;
}

Console.WriteLine("Result = {0}", calculated);
}

/* OUTPUT

An error occurred during division.
Result = 2147483647

*/Extracting Exception Information
As described earlier in this article, the throwing of an exception includes the generation of an exception object. the object has properties containing information describing the error that occurred. This information can be made available to the code within the catch code block by adding an exception declaration to the catch statement. The following example extends the previous code by declaring an object of the most generalised Exception class. All exceptions will still be caught but now the exception information may be used when generating the error message.

static void Main(string[] args)
{
int value = 50;
int divisor = 0;
int calculated;

try
{
calculated = value / divisor;
}
catch (Exception ex) // Catch any exception
{
Console.WriteLine("An error occurred during division.");
Console.WriteLine(ex.Message); // Report the error message
calculated = int.MaxValue;
}

Console.WriteLine("Result = {0}", calculated);
}

/* OUTPUT

An error occurred during division.
Attempted to divide by zero.
Result = 2147483647

*/The above example catches any error and populates an object of class Exception. The Message property of the object is then used to output an error description. This is one of several useful properties that are provided by the Exception class and all other derived exception types. Some of the most useful properties are:

Message. A string providing a brief description of the exception.
Source. A string containing the name of the program or object that caused the exception.
TargetSite. An object containing the name and other details of the method that caused the exception.
StackTrace. A string containing the complete stack of calls that led to the exception. This string allows the programmer to review each method call made up until the exception occurred. This is especially useful during testing and debugging.
InnerException. When one exception occurs as the direct result of another exception, the initial exception may be held in this property. This allows interrogation of both objects. The inner exception contains all of the standard properties including, potentially, a further InnerException. If there is no inner exception, this property is null.
NB: More specialised exception types may include further relevant information. For example, the ArgumentException and derived exceptions that are thrown when a parameter passed to a method is invalid include a 'ParamName' property detailing the parameter in question.

static void Main(string[] args)
{
int value = 50;
int divisor = 0;
int calculated;

try
{
calculated = value / divisor;
}
catch (Exception ex)
{
Console.WriteLine("Message: {0}\n", ex.Message);
Console.WriteLine("Source: {0}\n", ex.Source);
Console.WriteLine("TargetSite: {0}\n", ex.TargetSite.Name);
Console.WriteLine("StackTrace: {0}\n", ex.StackTrace);

calculated = int.MaxValue;
}

Console.WriteLine("Result = {0}", calculated);
}

/* OUTPUT

Message: Attempted to divide by zero.

Source: ConsoleApplication1

TargetSite: Void Main(System.String[])

StackTrace: at ConsoleApplication1.Program.Main(String[] args) in
C:\...\Program.cs:line 17

Result = 2147483647

*/Catching Specific Exceptions
So far, the examples described have included code to catch all exceptions. However, sometimes you will want to catch only a specific type of exception so that different problems can be handled in different ways. In order to catch a more specialised exception only, the correct class of exception is named in the catch statement. The following example uses this method to only catch division by zero. Any other exception would remain unhandled.

static void Main(string[] args)
{
int value = 50;
int divisor = 0;
int calculated;

try
{
calculated = value / divisor;
}
catch (DivideByZeroException ex) // Catch specific exception only
{
Console.WriteLine("Division by zero occurred.");
Console.WriteLine(ex.Message); // Report the error message
calculated = int.MaxValue;
}

Console.WriteLine("Result = {0}", calculated);
}

/* OUTPUT

Division by zero occurred.
Attempted to divide by zero.
Result = 2147483647

*/Catching specific exception types provides two benefits. Firstly, unexpected exceptions such as out of memory are not caught and misinterpreted or masked causing unexpected side effects. Secondly, any additional properties associated with the specialised exception class are made available in the catch block.

NB: If catching the exception type is enough and it is not necessary to interrogate the exception properties then there is no need to include a variable name for the exception object. The catch in the above example could be shortened to catch (DivideByZeroException) in such a situation.

Catching Multiple Exceptions
When developing complex routines, it is possible that many different types of exception could occur within a block of code or even within a single instruction. Each of these exceptions may require handling in a different manner. To permit this, multiple catch blocks may be added to a single try block. Each of these catch blocks handles a different kind of exception with the most specific exception types processed first and the most generic exceptions last.

Each catch block is checked in turn to see if the exception thrown is the same type as, or derives from, that declared in the catch statement. When a match is found, the code within the catch block is executed. Only one catch block's code is ever executed. Exceptions thrown that do not match any of the declared types remain unhandled.

The following example includes three catch blocks. The first handles any division by zero error. The second responds to a less general arithmetic exception but is not used if division by zero occurs. The final catch block does not specify the type of exception to catch and therefore is executed when any other type of exception occurs.

static void Main(string[] args)
{
int value = 50;
int divisor = 0;
int calculated;

try
{
calculated = value / divisor;
}
catch (DivideByZeroException) // Division by zero?
{
Console.WriteLine("Division by zero occurred.");
calculated = int.MaxValue;
}
catch (ArithmeticException) // Arithmetic exception?
{
Console.WriteLine("An arithmetic exception occured.");
calculated = int.MaxValue;
}
catch
{
Console.WriteLine("An unexpected exception occured.");
calculated = int.MaxValue;
}

Console.WriteLine("Result = {0}", calculated);
}

/* OUTPUT

Division by zero occurred.
Attempted to divide by zero.
Result = 2147483647

*/The Try / Catch / Finally Block
Sometimes it is necessary to ensure that some code executes whether an exception occurs or not. For example, if a file is opened before the try block, this file should be properly closed following successful processing or an exception.

C# defines an addition block that may be added to the end of the try / catch code structure. This is known as the finally block. The code within this section is guaranteed to be executed after the try / catch block, even if any of the statements in the try / catch block caused the current method to exit normally or by throwing a further exception.

static void Main(string[] args)
{
int value = 50;
int divisor = 0;
int calculated;

try
{
calculated = value / divisor;
}
catch
{
Console.WriteLine("An error occurred during division.");
calculated = int.MaxValue;
}
finally
{
Console.WriteLine("Clearing up any resources.");
}

Console.WriteLine("Result = {0}", calculated);
}

/* OUTPUT

An error occurred during division.
Clearing up any resources.
Result = 2147483647

*/It is possible to use try and finally together with no catch blocks. If an exception occurs when using such a structure it remains unhandled and is thrown to the calling routine or to the C# runtime system. However, the code in the finally block is executed whether an exception is raised or not.

0 Comments: