Let’s take some time to look at the parent exception class in the .NET framework – the System.Exception class. All exception classes are directly or indirectly derived from this class.
To see how to put this class to use, let’s quickly dive into a simple example where we can see all the exception handling constructs (try, catch, throw, and finally) in action. To start with fire up your favorite text editor (my personal favorite is Notepad) and type in the following code:
Code listing in C#
using System;
class HelloWorld
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Here we go...");
// Throw an exception
throw new Exception("Oops !. Your computer is on fire !!");
// This line should never execute
Console.WriteLine("How on earth did I get called ?");
}
catch(System.Exception ex)
{
// Display the error message
Console.WriteLine("Caught exception : {0}", ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In finally");
}
}
}
Code listing in VB.NET
Imports System
Module HelloWorld
' The Main entry point of the application
Sub Main()
Try
Console.WriteLine("Here we go...")
' Throw an exception
Throw New Exception("Oops !. Your computer is on fire !!")
' This line should never execute
Console.WriteLine("How on earth did I get called ?")
Catch ex As Exception
Console.WriteLine("Caught exception : {0}", ex.Message)
Finally
' This should always get called
Console.WriteLine("In finally")
End Try
End Sub
End Module
So what we have here is a try block that throws an exception by creating an instance of the System.Exception class with a descriptive error message. There’s a catch block to handle exceptions of type System.Exception. The code in the catch block just displays the error message. Finally, the finally block (no pun intended) logs a message that confirms that it did execute even though an error was thrown.
So let’s save this program to a file named HelloWorld (with the appropriate extension depending on the language you are using - .cs or .vb), the Hello World of Exception handling if you will.
To compile the C# program, type in the following command from the DOS command
line:
csc /target:exe HelloWorld.cs
To compile the VB.NET program, type in the following command from the DOS command line:
vbc /target:exe HelloWorld.vb
This will generate an executable named HelloWorld.exe. Run the program and here’s the output that you get:
Here we go...
Caught exception : Oops !. Your computer is on fire !!
In finally
You’ll notice that the exception that was thrown is caught by the catch block and that any statements that occur in the try block below the line that threw the exception are not executed. Notice that when you compiled the program in C#, the compiler generated the following warning:
Warning in C#
HelloWorld.cs(16,4): warning CS0162: Unreachable code detected
This goes to show that the C# compiler detected that the statement that follows the throw statement in the try block would never get executed because of the exception that was thrown, and thus warned us of unreachable code.
We saw how the Message property of the System.Exception class can be used to get a descriptive error message for the exception. Similarly, let’s examine the some of the other important properties of the System.Exception class. Let’s start by modifying the catch block in example that we just saw with the following code:
Code listing in C#
// Replace the catch block in our previous example with the following code
catch(System.Exception ex)
{
Console.WriteLine("Caught exception : {0}", ex.Message);
Console.WriteLine("Source of the exception is : {0}", ex.Source);
Console.WriteLine("Method that threw the exception is : {0}", ex.TargetSite.Name);
Console.WriteLine("Info on this exception is available at : {0}", ex.HelpLink);
Console.WriteLine("Stack trace of this exception: {0}", ex.StackTrace);
}
Code listing in VB.NET
' Replace the catch block in our previous example with the following code Catch ex As Exception
Console.WriteLine("Caught exception : {0}", ex.Message)
Console.WriteLine("Source of the exception is : {0}", ex.Source)
Console.WriteLine("Method that threw the exception is : {0}", _ ex.TargetSite.Name)
Console.WriteLine("Info on this exception is available at: {0}",ex.HelpLink)
Console.WriteLine("Stack trace of this exception: {0}",ex.StackTrace)
Finally
' Rest of the code goes here . . .
Compile and run the application and observe the output that you get:
Output in C#
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at:
Stack trace of this exception: at HelloWorld.Main(String[] args)
In finally
Output in VB.NET
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at:
Stack trace of this exception: at HelloWorld.Main()
In finally
You’ll notice that you can get rich information on the exception that occurred including details on the application and the method that threw the exception (through the Source and TargetSite properties respectively) and a complete stack trace of the exception in a string
representation (using the StackTrace property). Note that if you compile your code in
debug mode i.e. using the /debug+ compiler option, the Source and StackTrace properties
will show you the actual line numbers in the source code which raised the exception.
You’ll notice that the HelpLink property, which is supposed to provide a link to help file
or a URL that contains information on the exception that occurred, does not seem to
return anything. This is because we did not set this property when throwing the
exception. To do that you simply need to set the HelpLink property before raising the
exception. Here’s a snippet of code that shows how you can do that:
Code listing in C#
// Create an Exception
Exception exception = new Exception("Oops !. Your computer is on fire !!");
// Set the help file details
exception.HelpLink = "http://www.someurl.com/help/ComputerOnFireHelp.html";
// Throw the exception
throw exception;
Code listing in VB.NET
' Create an Exception
Dim excep as Exception = New Exception("Oops !. Your computer is on fire !!")
' Set the help file details
excep.HelpLink = "http://www.someurl.com/help/ComputerOnFireHelp.html"
' Throw the exception
Throw excep
Replacing the statement that throws the exception with the above 3 statements in our
example application, compiling it, and running it will now yield the following results:
Output in C#
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at: http://www.someurl.com/help/ComputerOnFireHelp.html
Stack trace of this exception: at HelloWorld.Main(String[] args)
In finally
Output in VB.NET
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at: http://www.someurl.com/help/ComputerOnFireHelp.html
Stack trace of this exception: at HelloWorld.Main()
In finally
There’s one other thing that you need to be aware of - The notion of an inner exception,
that you can access using the InnerException property of the main exception. So what
exactly is an inner exception? . Assume that you have a nice cool stock portal that allows
customers to manage their stocks and investments. The stock portal uses a database to
store data on customers and their portfolio. Now, let’s say that you encounter a database
specific error in your application. The last thing that you want to do is to display some
cryptic ADO or OLEDB messages in your web pages that your customers don’t care a
hang about. In such cases, you might have a catch handler to catch database specific
exceptions. What this catch handler would essentially do is to create a more generic
exception that is application specific (maybe an exception that tells the user that the site
encountered an internal error) and would assign the database specific exception to the
application-specific exception’s InnerException property. The catch handler then re-throws
this application-specific exception expecting that one of the outer catch blocks will handle
the generic exception. We’ll see how to re-throw exceptions in the section, Nesting
try/catch/finally blocks and re-throwing exceptions. Inner exceptions are very useful when
you are dealing with exceptions that occur in multiple tiers of typical enterprise
applications. This allows you to envelope specific exceptions that actually caused the
error into more application-specific exception types, and at the same time allows clients
to determine the specific exception type (InnerException) that caused the
application-specific exception to be thrown.
Now since we know more about the System.Exception class, let’s take a look at the types
of exceptions and how they can be classified. Broadly, there are two types of exceptions:
τ System exceptions (Exception classes derived from System.SystemException)
τ Application exceptions (Exception classes derived from
System.ApplicationException)
Understanding system exceptions:
System exceptions are pre-defined exceptions that ship with the .NET framework class
library. For example, the System.IO.IOException class, which is predefined exception in the
framework class library for handling input/output related errors on files, streams etc., is
derived from the System.SystemException class.
There are tons of other similar predefined system exception classes that are defined and
used in the FCL and which can be used in our applications as well. Let’s take a look at a
quick example on how to handle system exceptions in your application. We’ll use the
System.DivideByZeroException as our guinea pig here and simulate a situation where the
FCL throws this exception. We’ll handle this error and report the error to the user. Fire up
Notepad, and type in the following code:
Code listing in C#
using System;
class MyDecimalDivider
{
static void Main(string[] args)
{
try
{
// Trigger a divide by zero exception
Decimal dResult = Decimal.Divide(5,0);
// We should never get here
Console.WriteLine("Result is : {0}", dResult);
}
catch(DivideByZeroException exDivByZero)
{
Console.WriteLine("Caught Divide By Zero exception: {0}",
exDivByZero.Message);
}
catch(Exception ex)
{
Console.WriteLine("Caught exception: {0}",ex.Message);
}
finally
{
// Should always execute
Console.WriteLine("In finally");
}
}
}
Code listing in VB.NET
Imports System
Module MyDecimalDivider
Sub Main()
Try
' Trigger a divide by zero exception
Dim dResult as Decimal = Decimal.Divide(5,0)
' We should never get here
Console.WriteLine("Result is : {0}", dResult)
Catch exDivByZero As DivideByZeroException
Console.WriteLine("Caught Divide By Zero exception: {0}", _
exDivByZero.Message)
Catch ex As Exception
Console.WriteLine("Caught exception: {0}", ex.Message)
Finally
' Should always execute
Console.WriteLine("In finally")
End Try
End Sub
End Module
So essentially, what we’re doing here is simulating a DivideByZeroException by calling the
Divide() static method of the System.Decimal class and passing in a value of 0 for the
divisor. A quick look at the documentation for the Divide() method will tell you that the
method throws a DivideByZeroException when attempting to divide by 0. So we’re setting
up a catch block to handle exceptions of type System.DivideByZeroException.
Save the file to MyDecimalDivider (with the appropriate extension .cs or .vb depending on the
language that you are using). Let’s get down to compiling the application. Type the
following command from the DOS command prompt:
Compiling in C#
csc /target:exe MyDecimalDivider.cs
Compiling in VB.NET
vbc /target:exe MyDecimalDivider.vb
That takes care of generating an executable file named MyDecimalDivider.exe. Run the
program and observe the output:
Caught Divide By Zero exception: Attempted to divide by zero.
In finally
There we go. As seen above, the catch handler for the DivideByZeroException took care of
catching the exception that was raised when we attempted to divide 5 by 0. The
System.DivideByZeroException is just one of the many predefined system exception classes
in the FCL. For a complete list of the other system exception classes, swing by to:
http://msdn.microsoft.com/library/enus/
cpref/html/frlrfsystemsystemexceptionclasshierarchy.asp
Ordering catch handlers to filter exceptions:
Notice that we also have another catch block that handles the generic System.Exception. If
none of the other catch handlers can handle an exception raised, the System.Exception catch
handler will always lend a helping hand in catching and handling the exception, since the
rest of the exception types are derived from this class. So that brings us to another thing
that you need to remember - If you do not have a catch handler to handle a specific
exception type, say SomeException, but do have a catch hander that can handle a type that
is a super class of SomeException, then the catch handler associated with that super class
will be asked to handle the exception. In our example, even if we did not have the catch
handler for the System.DivideByZeroException, the catch handler for the System.Exception
would have been able to handle the exception, since System.DivideByZeroException inherits
from System.ArithmeticException, which in turn derives from System.SystemException and
hence System.Exception.
Keeping this in mind, it is important to understand that the order in which you place your
catch handlers plays a key role in determining the catch handler that will eventually
handle your exceptions. As a general rule, always place exception types of more derived
classes in an exception class hierarchy higher up in the chain and place base class (super
class) exception types lower down in the chain. To illustrate this, let’s slightly modify the
earlier divide by zero example and note down a few observations:
Modify the MyDecimalDivider.cs code sample as shown below to introduce a catch handler
for the System.ArithmeticException, which is the immediate base class of the
System.DivideByZeroException and place that catch handler above the catch handler that
handles the DivideByZeroException:
Code listing in C#
using System;
class MyDecimalDivider
{
static void Main(string[] args)
{
try
{
// Trigger a divide by zero exception
Decimal dResult = Decimal.Divide(5,0);
// We should never get here
Console.WriteLine("Result is : {0}", dResult);
}
catch(ArithmeticException exArithmetic)
{
Console.WriteLine("Caught Arithmetic exception: {0}",
exArithmetic.Message);
}
catch(DivideByZeroException exDivByZero)
{
Console.WriteLine("Caught Divide By Zero exception: {0}",
exDivByZero.Message);
}
catch(Exception ex)
{
Console.WriteLine("Caught exception: {0}", ex.Message);
}
finally
{
// Should always execute
Console.WriteLine("In finally");
}
}
}
Code listing in VB.NET
Imports System
Module MyDecimalDivider
Sub Main()
Try
' Trigger a divide by zero exception
Dim dResult as Decimal = Decimal.Divide(5,0)
' We should never get here
Console.WriteLine("Result is : {0}", dResult)
Catch exArithmetic As ArithmeticException
Console.WriteLine("Caught Arithmetic exception: {0}", _
exArithmetic.Message)
Catch exDivByZero As DivideByZeroException
Console.WriteLine("Caught Divide By Zero exception: {0}", _
exDivByZero.Message)
Catch ex As Exception
Console.WriteLine("Caught exception: {0}", ex.Message)
Finally
' Should always execute
Console.WriteLine("In finally")
End Try
End Sub
End Module
Now save the file and compile the modified MyDecimalDivider.cs/ MyDecimalDivider.vb in
the DOS command line using:
Compiling in C#
csc /target:exe MyDecimalDivider.cs
Compiling in VB.NET
vbc /target:exe MyDecimalDivider.vb
Run the application MyDecimalDivider.exe and observe the output:
Output in C#
MyDecimalDivider.cs(21,9): error CS0160: A previous catch clause already catches all exceptions of this or a super type
('System.ArithmeticException')
The error says it all. The ArithmeticException catch handler has been placed above the
catch handler that handles exception types of its subclass DivideByZeroException, which
effectively hides the catch handler for the DivideByZeroException.
Output in VB.NET
Caught Arithmetic exception: Attempted to divide by zero.
In finally
Since ArithmeticException‘s Catch handler has been placed above the Catch handler for its
subclass exception type DivideByZeroException, the Catch handler for the ArithmeticException
is asked to handle the error even though the actual exception type that was raised was
DivideByZeroException.
You’ll observe the same behavior if you place the System.Exception Catch handler above
any of the other catch handlers. In order to give DivideByZeroException’s Catch handler the
opportunity to handle the error, place it above the Catch handler that handles
ArithmeticException exceptions (DivideByZeroException’s super class). To summarize, place
Catch handler filters for specific exception types (sub classes) higher than the handlers for
the more generic exception types (base classes).
Code listing in C#
// Rest of the code omitted for brevity . . .
catch(DivideByZeroException exDivByZero)
{
Console.WriteLine("Caught Divide By Zero exception: {0}",
exDivByZero.Message);
}
catch(ArithmeticException exArithmetic)
{
Console.WriteLine("Caught Arithmetic exception: {0}", exArithmetic.Message);
}
catch(Exception ex)
{
Console.WriteLine("Caught exception: {0}", ex.Message);
}
Code listing in VB.NET
Try
// Rest of the code omitted for brevity . . .
Catch exDivByZero As DivideByZeroException
Console.WriteLine("Caught Divide By Zero exception: {0}", _
exDivByZero.Message)
Catch exArithmetic As ArithmeticException
Console.WriteLine("Caught Arithmetic exception: {0}", exArithmetic.Message)
Catch ex As Exception
Console.WriteLine("Caught exception: {0}", ex.Message)
Finally
' Should always execute
Console.WriteLine("In finally")
End Try
Now let’s try one more thing. We’ll remove the catch handler for the
DivideByZeroException, just leaving behind the catch handlers for the
System.ArithmeticException and the System.Exception classes.
Go ahead and modify the MyDecimalDivider code by commenting out the catch handler for
the DivideByZeroException. Compile and run the application. What output do you see this
time?
Caught Arithmetic exception: Attempted to divide by zero.
In finally
As shown above, though there was no catch handler for the DivideByZeroException that
was raised, the catch handler for ArithemeticException was able to catch the exception since
the ArithemeticException class happens to be a base class of the DivideByZeroException
class. Similarly, even if we didn’t have the catch handler for the ArithmeticException class,
the System.Exception catch handler would have still caught the exception (since it’s the
parent class for all exception types). So what happens if a DivideByZeroException is raised
and you don’t have any of the catch handlers (not even the System.Exception catch
handler)? . You guessed right – the exception would turn into an unhandled exception
crashing your application. Try this by removing the try block and all the catch-finally
handlers and call Decimal.Divide() by passing a value of 0 for the divisor and notice what
happens:
Output in C#
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at System.Decimal.Divide(Decimal d1, Decimal d2)
at MyDecimalDivider.Main(String[] args)
Output in VB.NET
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at System.Decimal.Divide(Decimal d1, Decimal d2)
at MyDecimalDivider.Main()
To see how to put this class to use, let’s quickly dive into a simple example where we can see all the exception handling constructs (try, catch, throw, and finally) in action. To start with fire up your favorite text editor (my personal favorite is Notepad) and type in the following code:
Code listing in C#
using System;
class HelloWorld
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Here we go...");
// Throw an exception
throw new Exception("Oops !. Your computer is on fire !!");
// This line should never execute
Console.WriteLine("How on earth did I get called ?");
}
catch(System.Exception ex)
{
// Display the error message
Console.WriteLine("Caught exception : {0}", ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In finally");
}
}
}
Code listing in VB.NET
Imports System
Module HelloWorld
' The Main entry point of the application
Sub Main()
Try
Console.WriteLine("Here we go...")
' Throw an exception
Throw New Exception("Oops !. Your computer is on fire !!")
' This line should never execute
Console.WriteLine("How on earth did I get called ?")
Catch ex As Exception
Console.WriteLine("Caught exception : {0}", ex.Message)
Finally
' This should always get called
Console.WriteLine("In finally")
End Try
End Sub
End Module
So what we have here is a try block that throws an exception by creating an instance of the System.Exception class with a descriptive error message. There’s a catch block to handle exceptions of type System.Exception. The code in the catch block just displays the error message. Finally, the finally block (no pun intended) logs a message that confirms that it did execute even though an error was thrown.
So let’s save this program to a file named HelloWorld (with the appropriate extension depending on the language you are using - .cs or .vb), the Hello World of Exception handling if you will.
To compile the C# program, type in the following command from the DOS command
line:
csc /target:exe HelloWorld.cs
To compile the VB.NET program, type in the following command from the DOS command line:
vbc /target:exe HelloWorld.vb
This will generate an executable named HelloWorld.exe. Run the program and here’s the output that you get:
Here we go...
Caught exception : Oops !. Your computer is on fire !!
In finally
You’ll notice that the exception that was thrown is caught by the catch block and that any statements that occur in the try block below the line that threw the exception are not executed. Notice that when you compiled the program in C#, the compiler generated the following warning:
Warning in C#
HelloWorld.cs(16,4): warning CS0162: Unreachable code detected
This goes to show that the C# compiler detected that the statement that follows the throw statement in the try block would never get executed because of the exception that was thrown, and thus warned us of unreachable code.
We saw how the Message property of the System.Exception class can be used to get a descriptive error message for the exception. Similarly, let’s examine the some of the other important properties of the System.Exception class. Let’s start by modifying the catch block in example that we just saw with the following code:
Code listing in C#
// Replace the catch block in our previous example with the following code
catch(System.Exception ex)
{
Console.WriteLine("Caught exception : {0}", ex.Message);
Console.WriteLine("Source of the exception is : {0}", ex.Source);
Console.WriteLine("Method that threw the exception is : {0}", ex.TargetSite.Name);
Console.WriteLine("Info on this exception is available at : {0}", ex.HelpLink);
Console.WriteLine("Stack trace of this exception: {0}", ex.StackTrace);
}
Code listing in VB.NET
' Replace the catch block in our previous example with the following code Catch ex As Exception
Console.WriteLine("Caught exception : {0}", ex.Message)
Console.WriteLine("Source of the exception is : {0}", ex.Source)
Console.WriteLine("Method that threw the exception is : {0}", _ ex.TargetSite.Name)
Console.WriteLine("Info on this exception is available at: {0}",ex.HelpLink)
Console.WriteLine("Stack trace of this exception: {0}",ex.StackTrace)
Finally
' Rest of the code goes here . . .
Compile and run the application and observe the output that you get:
Output in C#
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at:
Stack trace of this exception: at HelloWorld.Main(String[] args)
In finally
Output in VB.NET
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at:
Stack trace of this exception: at HelloWorld.Main()
In finally
You’ll notice that you can get rich information on the exception that occurred including details on the application and the method that threw the exception (through the Source and TargetSite properties respectively) and a complete stack trace of the exception in a string
representation (using the StackTrace property). Note that if you compile your code in
debug mode i.e. using the /debug+ compiler option, the Source and StackTrace properties
will show you the actual line numbers in the source code which raised the exception.
You’ll notice that the HelpLink property, which is supposed to provide a link to help file
or a URL that contains information on the exception that occurred, does not seem to
return anything. This is because we did not set this property when throwing the
exception. To do that you simply need to set the HelpLink property before raising the
exception. Here’s a snippet of code that shows how you can do that:
Code listing in C#
// Create an Exception
Exception exception = new Exception("Oops !. Your computer is on fire !!");
// Set the help file details
exception.HelpLink = "http://www.someurl.com/help/ComputerOnFireHelp.html";
// Throw the exception
throw exception;
Code listing in VB.NET
' Create an Exception
Dim excep as Exception = New Exception("Oops !. Your computer is on fire !!")
' Set the help file details
excep.HelpLink = "http://www.someurl.com/help/ComputerOnFireHelp.html"
' Throw the exception
Throw excep
Replacing the statement that throws the exception with the above 3 statements in our
example application, compiling it, and running it will now yield the following results:
Output in C#
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at: http://www.someurl.com/help/ComputerOnFireHelp.html
Stack trace of this exception: at HelloWorld.Main(String[] args)
In finally
Output in VB.NET
Here we go...
Caught exception : Oops !. Your computer is on fire !!
Source of the exception is : HelloWorld
Method that threw the exception is : Main
Info on this exception is available at: http://www.someurl.com/help/ComputerOnFireHelp.html
Stack trace of this exception: at HelloWorld.Main()
In finally
There’s one other thing that you need to be aware of - The notion of an inner exception,
that you can access using the InnerException property of the main exception. So what
exactly is an inner exception? . Assume that you have a nice cool stock portal that allows
customers to manage their stocks and investments. The stock portal uses a database to
store data on customers and their portfolio. Now, let’s say that you encounter a database
specific error in your application. The last thing that you want to do is to display some
cryptic ADO or OLEDB messages in your web pages that your customers don’t care a
hang about. In such cases, you might have a catch handler to catch database specific
exceptions. What this catch handler would essentially do is to create a more generic
exception that is application specific (maybe an exception that tells the user that the site
encountered an internal error) and would assign the database specific exception to the
application-specific exception’s InnerException property. The catch handler then re-throws
this application-specific exception expecting that one of the outer catch blocks will handle
the generic exception. We’ll see how to re-throw exceptions in the section, Nesting
try/catch/finally blocks and re-throwing exceptions. Inner exceptions are very useful when
you are dealing with exceptions that occur in multiple tiers of typical enterprise
applications. This allows you to envelope specific exceptions that actually caused the
error into more application-specific exception types, and at the same time allows clients
to determine the specific exception type (InnerException) that caused the
application-specific exception to be thrown.
Now since we know more about the System.Exception class, let’s take a look at the types
of exceptions and how they can be classified. Broadly, there are two types of exceptions:
τ System exceptions (Exception classes derived from System.SystemException)
τ Application exceptions (Exception classes derived from
System.ApplicationException)
Understanding system exceptions:
System exceptions are pre-defined exceptions that ship with the .NET framework class
library. For example, the System.IO.IOException class, which is predefined exception in the
framework class library for handling input/output related errors on files, streams etc., is
derived from the System.SystemException class.
There are tons of other similar predefined system exception classes that are defined and
used in the FCL and which can be used in our applications as well. Let’s take a look at a
quick example on how to handle system exceptions in your application. We’ll use the
System.DivideByZeroException as our guinea pig here and simulate a situation where the
FCL throws this exception. We’ll handle this error and report the error to the user. Fire up
Notepad, and type in the following code:
Code listing in C#
using System;
class MyDecimalDivider
{
static void Main(string[] args)
{
try
{
// Trigger a divide by zero exception
Decimal dResult = Decimal.Divide(5,0);
// We should never get here
Console.WriteLine("Result is : {0}", dResult);
}
catch(DivideByZeroException exDivByZero)
{
Console.WriteLine("Caught Divide By Zero exception: {0}",
exDivByZero.Message);
}
catch(Exception ex)
{
Console.WriteLine("Caught exception: {0}",ex.Message);
}
finally
{
// Should always execute
Console.WriteLine("In finally");
}
}
}
Code listing in VB.NET
Imports System
Module MyDecimalDivider
Sub Main()
Try
' Trigger a divide by zero exception
Dim dResult as Decimal = Decimal.Divide(5,0)
' We should never get here
Console.WriteLine("Result is : {0}", dResult)
Catch exDivByZero As DivideByZeroException
Console.WriteLine("Caught Divide By Zero exception: {0}", _
exDivByZero.Message)
Catch ex As Exception
Console.WriteLine("Caught exception: {0}", ex.Message)
Finally
' Should always execute
Console.WriteLine("In finally")
End Try
End Sub
End Module
So essentially, what we’re doing here is simulating a DivideByZeroException by calling the
Divide() static method of the System.Decimal class and passing in a value of 0 for the
divisor. A quick look at the documentation for the Divide() method will tell you that the
method throws a DivideByZeroException when attempting to divide by 0. So we’re setting
up a catch block to handle exceptions of type System.DivideByZeroException.
Save the file to MyDecimalDivider (with the appropriate extension .cs or .vb depending on the
language that you are using). Let’s get down to compiling the application. Type the
following command from the DOS command prompt:
Compiling in C#
csc /target:exe MyDecimalDivider.cs
Compiling in VB.NET
vbc /target:exe MyDecimalDivider.vb
That takes care of generating an executable file named MyDecimalDivider.exe. Run the
program and observe the output:
Caught Divide By Zero exception: Attempted to divide by zero.
In finally
There we go. As seen above, the catch handler for the DivideByZeroException took care of
catching the exception that was raised when we attempted to divide 5 by 0. The
System.DivideByZeroException is just one of the many predefined system exception classes
in the FCL. For a complete list of the other system exception classes, swing by to:
http://msdn.microsoft.com/library/enus/
cpref/html/frlrfsystemsystemexceptionclasshierarchy.asp
Ordering catch handlers to filter exceptions:
Notice that we also have another catch block that handles the generic System.Exception. If
none of the other catch handlers can handle an exception raised, the System.Exception catch
handler will always lend a helping hand in catching and handling the exception, since the
rest of the exception types are derived from this class. So that brings us to another thing
that you need to remember - If you do not have a catch handler to handle a specific
exception type, say SomeException, but do have a catch hander that can handle a type that
is a super class of SomeException, then the catch handler associated with that super class
will be asked to handle the exception. In our example, even if we did not have the catch
handler for the System.DivideByZeroException, the catch handler for the System.Exception
would have been able to handle the exception, since System.DivideByZeroException inherits
from System.ArithmeticException, which in turn derives from System.SystemException and
hence System.Exception.
Keeping this in mind, it is important to understand that the order in which you place your
catch handlers plays a key role in determining the catch handler that will eventually
handle your exceptions. As a general rule, always place exception types of more derived
classes in an exception class hierarchy higher up in the chain and place base class (super
class) exception types lower down in the chain. To illustrate this, let’s slightly modify the
earlier divide by zero example and note down a few observations:
Modify the MyDecimalDivider.cs code sample as shown below to introduce a catch handler
for the System.ArithmeticException, which is the immediate base class of the
System.DivideByZeroException and place that catch handler above the catch handler that
handles the DivideByZeroException:
Code listing in C#
using System;
class MyDecimalDivider
{
static void Main(string[] args)
{
try
{
// Trigger a divide by zero exception
Decimal dResult = Decimal.Divide(5,0);
// We should never get here
Console.WriteLine("Result is : {0}", dResult);
}
catch(ArithmeticException exArithmetic)
{
Console.WriteLine("Caught Arithmetic exception: {0}",
exArithmetic.Message);
}
catch(DivideByZeroException exDivByZero)
{
Console.WriteLine("Caught Divide By Zero exception: {0}",
exDivByZero.Message);
}
catch(Exception ex)
{
Console.WriteLine("Caught exception: {0}", ex.Message);
}
finally
{
// Should always execute
Console.WriteLine("In finally");
}
}
}
Code listing in VB.NET
Imports System
Module MyDecimalDivider
Sub Main()
Try
' Trigger a divide by zero exception
Dim dResult as Decimal = Decimal.Divide(5,0)
' We should never get here
Console.WriteLine("Result is : {0}", dResult)
Catch exArithmetic As ArithmeticException
Console.WriteLine("Caught Arithmetic exception: {0}", _
exArithmetic.Message)
Catch exDivByZero As DivideByZeroException
Console.WriteLine("Caught Divide By Zero exception: {0}", _
exDivByZero.Message)
Catch ex As Exception
Console.WriteLine("Caught exception: {0}", ex.Message)
Finally
' Should always execute
Console.WriteLine("In finally")
End Try
End Sub
End Module
Now save the file and compile the modified MyDecimalDivider.cs/ MyDecimalDivider.vb in
the DOS command line using:
Compiling in C#
csc /target:exe MyDecimalDivider.cs
Compiling in VB.NET
vbc /target:exe MyDecimalDivider.vb
Run the application MyDecimalDivider.exe and observe the output:
Output in C#
MyDecimalDivider.cs(21,9): error CS0160: A previous catch clause already catches all exceptions of this or a super type
('System.ArithmeticException')
The error says it all. The ArithmeticException catch handler has been placed above the
catch handler that handles exception types of its subclass DivideByZeroException, which
effectively hides the catch handler for the DivideByZeroException.
Output in VB.NET
Caught Arithmetic exception: Attempted to divide by zero.
In finally
Since ArithmeticException‘s Catch handler has been placed above the Catch handler for its
subclass exception type DivideByZeroException, the Catch handler for the ArithmeticException
is asked to handle the error even though the actual exception type that was raised was
DivideByZeroException.
You’ll observe the same behavior if you place the System.Exception Catch handler above
any of the other catch handlers. In order to give DivideByZeroException’s Catch handler the
opportunity to handle the error, place it above the Catch handler that handles
ArithmeticException exceptions (DivideByZeroException’s super class). To summarize, place
Catch handler filters for specific exception types (sub classes) higher than the handlers for
the more generic exception types (base classes).
Code listing in C#
// Rest of the code omitted for brevity . . .
catch(DivideByZeroException exDivByZero)
{
Console.WriteLine("Caught Divide By Zero exception: {0}",
exDivByZero.Message);
}
catch(ArithmeticException exArithmetic)
{
Console.WriteLine("Caught Arithmetic exception: {0}", exArithmetic.Message);
}
catch(Exception ex)
{
Console.WriteLine("Caught exception: {0}", ex.Message);
}
Code listing in VB.NET
Try
// Rest of the code omitted for brevity . . .
Catch exDivByZero As DivideByZeroException
Console.WriteLine("Caught Divide By Zero exception: {0}", _
exDivByZero.Message)
Catch exArithmetic As ArithmeticException
Console.WriteLine("Caught Arithmetic exception: {0}", exArithmetic.Message)
Catch ex As Exception
Console.WriteLine("Caught exception: {0}", ex.Message)
Finally
' Should always execute
Console.WriteLine("In finally")
End Try
Now let’s try one more thing. We’ll remove the catch handler for the
DivideByZeroException, just leaving behind the catch handlers for the
System.ArithmeticException and the System.Exception classes.
Go ahead and modify the MyDecimalDivider code by commenting out the catch handler for
the DivideByZeroException. Compile and run the application. What output do you see this
time?
Caught Arithmetic exception: Attempted to divide by zero.
In finally
As shown above, though there was no catch handler for the DivideByZeroException that
was raised, the catch handler for ArithemeticException was able to catch the exception since
the ArithemeticException class happens to be a base class of the DivideByZeroException
class. Similarly, even if we didn’t have the catch handler for the ArithmeticException class,
the System.Exception catch handler would have still caught the exception (since it’s the
parent class for all exception types). So what happens if a DivideByZeroException is raised
and you don’t have any of the catch handlers (not even the System.Exception catch
handler)? . You guessed right – the exception would turn into an unhandled exception
crashing your application. Try this by removing the try block and all the catch-finally
handlers and call Decimal.Divide() by passing a value of 0 for the divisor and notice what
happens:
Output in C#
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at System.Decimal.Divide(Decimal d1, Decimal d2)
at MyDecimalDivider.Main(String[] args)
Output in VB.NET
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at System.Decimal.Divide(Decimal d1, Decimal d2)
at MyDecimalDivider.Main()