When the exception is thrown, execution flow is transferred to the
catch
block that is capable of handling the exception thus skipping the rest of the code below the line that
threw the exception in the
try
block. The code in the
catch
block handles the exception appropriately and then execution flow moves to the code in the
finally
block.
7.4 System.Exception: The mother of all exceptions
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 Mainstring[] args
{ try
{ Console.WriteLineHere we go...;
Throw an exception throw new ExceptionOops . Your computer is on fire ;
This line should never execute Console.WriteLineHow on earth did I get called ?;
} catchSystem.Exception ex
{ Display the error message
Console.WriteLineCaught exception : {0}, ex.Message; }
finally {
This should always get called Console.WriteLineIn finally;
} }
}
Code listing in VB.NET
Imports System Module HelloWorld
The Main entry point of the application Sub Main
Try Console.WriteLineHere we go...
Throw an exception Throw New ExceptionOops . Your computer is on fire
This line should never execute Console.WriteLineHow on earth did I get called ?
Catch ex As Exception Console.WriteLineCaught exception : {0}, ex.Message
Finally This should always get called
Console.WriteLineIn 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.cs16,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 catchSystem.Exception ex
{ Console.WriteLineCaught exception : {0}, ex.Message;
Console.WriteLineSource of the exception is : {0}, ex.Source; Console.WriteLineMethod that threw the exception is : {0},
ex.TargetSite.Name; Console.WriteLineInfo on this exception is available at : {0},
ex.HelpLink; Console.WriteLineStack 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.WriteLineCaught exception : {0}, ex.Message Console.WriteLineSource of the exception is : {0}, ex.Source
Console.WriteLineMethod that threw the exception is : {0}, _ ex.TargetSite.Name
Console.WriteLineInfo on this exception is available at: {0},ex.HelpLink Console.WriteLineStack 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.MainString[] 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 ExceptionOops . Your computer is on fire ;
Set the help file details exception.HelpLink = http:www.someurl.comhelpComputerOnFireHelp.html;
Throw the exception throw exception;
Code listing in VB.NET
Create an Exception Dim excep as Exception = New ExceptionOops . Your computer is on fire
Set the help file details excep.HelpLink = http:www.someurl.comhelpComputerOnFireHelp.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.comhelpComputerOnFireHelp.html Stack trace of this exception: at HelloWorld.MainString[] 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.comhelpComputerOnFireHelp.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 trycatchfinally 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 inputoutput 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 Mainstring[] args
{ try
{ Trigger a divide by zero exception
Decimal dResult = Decimal.Divide5,0; We should never get here
Console.WriteLineResult is : {0}, dResult; }
catchDivideByZeroException exDivByZero {
Console.WriteLineCaught Divide By Zero exception: {0}, exDivByZero.Message;
} catchException ex
{ Console.WriteLineCaught exception: {0},ex.Message;
} finally
{ Should always execute
Console.WriteLineIn finally; }
} }
Code listing in VB.NET
Imports System Module MyDecimalDivider
Sub Main Try
Trigger a divide by zero exception Dim dResult as Decimal = Decimal.Divide5,0
We should never get here Console.WriteLineResult is : {0}, dResult
Catch exDivByZero As DivideByZeroException Console.WriteLineCaught Divide By Zero exception: {0}, _
exDivByZero.Message Catch ex As Exception
Console.WriteLineCaught exception: {0}, ex.Message Finally
Should always execute Console.WriteLineIn 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.comlibraryen- uscprefhtmlfrlrfsystemsystemexceptionclasshierarchy.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 Mainstring[] args
{ try
{ Trigger a divide by zero exception
Decimal dResult = Decimal.Divide5,0; We should never get here
Console.WriteLineResult is : {0}, dResult; }
catchArithmeticException exArithmetic {
Console.WriteLineCaught Arithmetic exception: {0}, exArithmetic.Message;
} catchDivideByZeroException exDivByZero
{ Console.WriteLineCaught Divide By Zero exception: {0},
exDivByZero.Message; }
catchException ex {
Console.WriteLineCaught exception: {0}, ex.Message; }
finally {
Should always execute Console.WriteLineIn finally;
} }
}
Code listing in VB.NET
Imports System Module MyDecimalDivider
Sub Main Try
Trigger a divide by zero exception Dim dResult as Decimal = Decimal.Divide5,0
We should never get here Console.WriteLineResult is : {0}, dResult
Catch exArithmetic As ArithmeticException Console.WriteLineCaught Arithmetic exception: {0}, _
exArithmetic.Message Catch exDivByZero As DivideByZeroException
Console.WriteLineCaught Divide By Zero exception: {0}, _ exDivByZero.Message
Catch ex As Exception Console.WriteLineCaught exception: {0}, ex.Message
Finally Should always execute
Console.WriteLineIn 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.cs21,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 . . . catchDivideByZeroException exDivByZero
{ Console.WriteLineCaught Divide By Zero exception: {0},
exDivByZero.Message; }
catchArithmeticException exArithmetic {
Console.WriteLineCaught Arithmetic exception: {0}, exArithmetic.Message; }
catchException ex {
Console.WriteLineCaught exception: {0}, ex.Message; }
Code listing in VB.NET
Try Rest of the code omitted for brevity . . .
Catch exDivByZero As DivideByZeroException Console.WriteLineCaught Divide By Zero exception: {0}, _
exDivByZero.Message Catch exArithmetic As ArithmeticException
Console.WriteLineCaught Arithmetic exception: {0}, exArithmetic.Message Catch ex As Exception
Console.WriteLineCaught exception: {0}, ex.Message Finally
Should always execute Console.WriteLineIn 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.DivideDecimal d1, Decimal d2
at MyDecimalDivider.MainString[] args
Output in VB.NET
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero. at System.Decimal.DivideDecimal d1, Decimal d2
at MyDecimalDivider.Main
7.5 Handling exceptions that are not System.Exception compliant