1. Introduction to Exceptions
An Exception
is an object delivered by the Exception
class. This Exception
class is exposed by the System.Exception
namespace. Exception
s are used to avoid system failure in an unexpected manner. Exception
handles the failure situation that may arise. All the exception
s in the .NET Framework are derived from the System.Exception
class.
To understand exception
, we need to know two basic things:
- Somebody sees a failure situation that happened and throws an exception by packing the valid information.
- Somebody who knows that a failure may happen catches the exception thrown. We call it
Exception
Handler.
Below is a simple example, which shows what happens when an Exception
is not handled:
class ExceptionsEx
{
public void StartProgram()
{
Console.WriteLine("Making Call to F1() " );
F1();
Console.WriteLine("Successfully returned from F1() " );
}
public void F1()
{
Console.WriteLine("Making Call to F2() " );
throw new System.Exception();
F2();
Console.WriteLine("Successfully returned from F2() " );
}
public void F2()
{
Console.WriteLine("Inside F2 " );
}
[STAThread]
static void Main(string[] args)
{
ExceptionsEx app = new ExceptionsEx();
app.StartProgram();
}
}
In the above code, look at the function F1 that throws an Exception
. But, there is no handler to deal with the thrown exception
. This situation is called an Un-Handled exception
situation. When you execute the code, you get an unhandled exception
dialog.
The above dialog is shown in debug mode, so you may get a chance to break the execution to see where the exception is thrown (or) continue ignoring the exception
(not advisable).
In release mode, you will get un-handled Exception
in the form of Application crash.
So, how do we avoid Un-Handled Exception
? Simple, handle it.
2. Handling Exception
To handle the exception, we need to place the code within the try
block. When an exception occurs inside the try
block, the control looks for the catch
block and raises an exception that is handled in the catch
block. Below is the simple skeleton for the try
and catch
block:
try
{
}
catch
{
}
The above skeleton handles any exception
. But, the main disadvantage is that we don’t know what exception
is raised and who raised the exception
. Below is the example that handles the Exception
raised by the function F1 and avoids the crash:
class ExceptionsEx
{
public void StartProgram()
{
Console.WriteLine("Making Call to F1() " );
try
{
F1();
}
catch
{
Console.WriteLine("Some Exception Occurred.
I don't know what Exception it is and where it Occurred. Sorry!");
}
Console.WriteLine("Successfully returned from F1() " );
}
public void F1()
{
Console.WriteLine("Making Call to F2() " );
throw new System.Exception();
Console.WriteLine("Successfully returned from F2() " );
}
[STAThread]
static void Main(string[] args)
{
ExceptionsEx app = new ExceptionsEx();
app.StartProgram();
}
}
3. Exception Bubbling
In the above example, we saw that Exception
is handled in the catch
block. But, the function call order is simple (Call Stack) that is; StartProgram
calls the function F1 and F1 raised exception
is handled in the catch
block of the StartProgram
.
Imagine the situation what happens if there are multiple nested function calls, and exception
occurred in the fourth or fifth nested call. Look at the picture below:
F1()
: Calls F2
within the try
block and handles the exception in catch
block
F2()
: Makes a call to the function F3
. But it neither wraps the call for F3
in the try
block nor has the exception handler
F3()
: Raises an Exception
Note, when the exception is thrown by the function F3
, even though the caller is F2
, as there is no catch
handler, the execution comes out of F2
and enters the catch
block of F1
. Travelling back from F3
->F2
->F1
is known as Stack Unwind. And exception occurred in F3
is handled in F1
even when there is no handler at F2
is known as Exception
Bubbling.
Below is the example that demonstrates the Exception
Bubbling:
using System;
namespace ExceptionHandling
{
class ExceptionsEx
{
public void StartProgram()
{
Console.WriteLine("Making Call to F1() " );
try
{
F1();
}
catch
{
Console.WriteLine("Some Exception Occurred.
I don't know what Exception it is and where it Occurred. Sorry!");
}
Console.WriteLine("Successfully returned from F1() " );
}
public void F1()
{
Console.WriteLine("Making Call to F2() " );
F2();
Console.WriteLine("Successfully returned from F2() " );
}
public void F2()
{
Console.WriteLine("Making Call to F2() " );
F3();
Console.WriteLine("Successfully returned from F2() " );
}
public void F3()
{
Console.WriteLine("Inside F3 " );
throw new System.Exception();
}
[STAThread]
static void Main(string[] args)
{
ExceptionsEx app = new ExceptionsEx();
app.StartProgram();
}
}
}
4. The Importance of Finally Block
In the above example, we saw that when an exception
occurs, we directly jump back on the call stack and travel back searching for the catch
handler. What about the piece of code that comes next to the exception raising code? If we do releasing resource and releasing the heap memory in the next couple of statements, that will not get reached. Right?
Finally
block is the solution for this. Now look at the improved exception
-handling skeleton below:
try
{
}
catch
{
}
finally
{
}
Whatever happens inside the try
block, it is guaranteed that finally
block gets executed. Hence, all resource releases should be in the finally
block. Have a look at the below picture:
Exception
raised at function F3
is handled in the function F2
. Look at the try
block, I marked two blocks of code before exception
and code after exception
. It is obvious that when an exception
raised, the set of code inside the code after exception is never executed. If resources are allocated in code before exception and the allocated resources are released in code after exception, we do have a resource leak for each exception
occurred. Also, think about business logic that needs to revert back apart from the resource leak. This is why finally
block is introduced.
Whether an Exception
occurs or not, the code inside finally
block always gets executed. So you can keep all the cleaning code inside the finally
block. The attached sample solution explains the above situation. Put a break point in the very first statement of the function public void StartProgram()
and examine the situation explained above. Note, the usage of try
block with only finally
and without catch
in function F2
. Think about it.
5. What is Exception Class?
This class is provided by .NET Framework to handle any exception
that occurred. The Exception
class is the base class for all other Exception
class provided by .NET Framework.
The exception object has some important properties. Some of then are given below:
Property |
Usage |
Message |
Gives detailed information about the message |
StackTrace |
Gives the function stack to show where exception is thrown |
Targetsite |
Shows which method throws the exception |
In the previous example, we only used the catch
block and missed all the above said information. The exception
object is thrown by the piece of code, which raises an Exception
and the handler code catches that Exception
object and makes use of the information packed in it. Consider the below example:
void SomefunctionX()
{
throw new DivideByZeroException();
}
void SomeFunctionY()
{
try
{
SomefunctionX();
}
catch (DivideByZeroException Ex)
{
}
}
The example was shown just to understand from where the object that is used in the catch
block is coming from. Hope, now you know the exception
instance created on the throw
statement caught in the catch
block and used. Note that, the base class of DivideByZeroException
is ArithmaticException
, which is derived from the System.Exception
. Now the question is, May I use ArithmaticException
in the catch
block? Yes. Why not? That is the beauty of polymorphism. When you do, always keep in mind that you are moving from more specialized information thrown to the generalized one.
6. Handler for Multiple Exceptions
A code placed in the try
block can raise different kinds of exception. It can be say, a Divide by Zero or a Network System Access Denied or a File not exists. How do you handle all the exceptions? The answer is, you can place multiple catch
blocks for a try
block. When an exception is thrown, type of that exception is examined against each catch
block. When a match occurs (even polymorph), the exception
enters into the catch
block and gets handled.
Let me walk through an example, which will explain the following to you:
- Make use of
Exception
object in the catch
block
- Using multiple
catch
blocks for different exception
s
- The importance order of the
catch
blocks
1) Program enters the main function and calls the member function StartProgram
after creating the object of type ExceptionsP2
.
[STAThread]
public static void Main(string[] args)
{
ExceptionsP2 start = new ExceptionsP2();
start.StartProgram();
}
2) The StartProgram
function makes a call to Calculate
function. And this call is placed in the try
block as it is expected that there be some arithmetic exception and possibly a Divide by Zero exception. Two catch
blocks are placed for the try
block of code. One is DivideByZeroException
and other one is System.Exception
. Inside each catch
block, the exception object caught was used to display the Exception
message, and function call stack of where the exception
raised. Below is the code:
public void StartProgram()
{
Console.WriteLine("Calling the Function Calculate");
try
{
Calculate();
}
catch (DivideByZeroException Ex)
{
Console.WriteLine("Divide By Zero.
Look at the Call stack for More information");
Console.WriteLine("Packed Message: " + Ex.Message );
Console.WriteLine("Thrown By: " + Ex.TargetSite );
Console.Write("Call Stack: " + Ex.StackTrace );
}
catch (Exception Ex)
{
Console.WriteLine("General Exception Occurred");
Console.WriteLine("Packed Message: " + Ex.Message );
Console.Write("Call Stack: " + Ex.StackTrace );
}
}
3) The calculate is just an intermediate function which makes a call to Divide
. Note how the stack rewind and exception bubbling is happening. Below is the function which makes a call to divide, and does not have any Exception handler code:
public void Calculate()
{
Console.WriteLine("Calling Divide Function ");
Divide();
}
4) The divide
function tries to divide a number by zero. The .NET environment detects the situation and raises the exception for us. Note there is no throw
statement here. In my previous examples (actually in the previous part), I forced the system to throw an Exception
for my demonstration purpose. We usually use the throw
statement for Custom defined exceptions. The .NET environment is smart enough to detect the exception at right time and raise it. Below is the code for divide:
public void Divide()
{
int x = 10;
int y = 0;
y = x / y;
}
7. Closing Notes
The DivideByZeroException
thrown in the Divide
function is caught by the StartProgram
function. Then the exception
object is matched against the first catch
block. As there is a match, the exception
enters that catch
block.
What happens if System.Exception catch
block is before the DivideByZeroException
? We do enter the System.Exception catch
block even though a more perfect match exists. Because, the exception
caught is matched from top to bottom. As the System.Exception
is on the top (that is; Before DivideByZero
) and the caught Exception DivideByZeroException
is polymorphically a System.Exception
the execution control just enters the System.Exception catch
block.
So always keep in mind to place more specific catch
statement first and move down with more Generalized Exception
s. Look at the code below how a smart developer placed his catch
block based on what his Team Lead told.
Lead: “The code block may raise a DivideByZeroException
or Any ArithmeticException
. It is possible to have FileNotFound Exception
also. These are the possibilities of Exception
I am seeing when I am reviewing your code. But, for safety, try to handle all exception
s in the world and it is OK if it is not more specific”.
That's all, the Lead went to a meeting.
Forget what meeting he is going for, see how the developer placed his catch
block:
try
{
}
catch (System.FileNotFoundException Ex)
{
}
catch( System.DivideByZeroException Ex)
{
}
catch (System.ArithmeticException Ex)
{
}
catch(System.Exception Ex)
{
}
finally
{
}
Look at the comments and I hope it is self-explanatory.
See you all, I hope this article explained something to you about Exception
Handling in C#.
jgauffin gave a nice point about why you should catch only the specific exception
.
Look at his Blog (jgauffin) for more information: Click here to visit.