Objectives
- Definition of "Exception".
- Exception class Hierarchy.
- Try/Catch Block.
- State the purpose of a "finally" block.
- Creating Custom Exceptions and Throw an Exception.
Introduction
C# and the .NET CLR use exceptions to indicate that an error has occurred during program execution i.e. at runtime. As program complexity increases, the possibility of something unusual occuring during execution increases. Exception handling helps applications trap and respond to exceptional events in a predictable and robust manner.
This chapter shows and explains how to properly handle existing exceptions using try, catch, finally and throw blocks and how to create your own custom exceptions.
What is an Exception
An Exception is an abnormal event or error condition that exists in the technical execution of a particular statement that occurs during program execution. Exceptions can occur in the code you write (internal) or external code (DLL) that your program calls. Some examples of error conditions include the following:
- File I/O related priblems
- Network communication problems
- Element access that is beyond the bound of an array
- Running out of memory during program execution
- Issue when doing a Divide-by-zero operation
CLR Exception Handling Mechanism
When an exception occurs in a program, the .NET framework runtime handles the problem by creating an exception object, populates it with information related to the error and passes it on to any fault handling code. C# does not force you to use try/catch blocks. The .NET runtime will try its best and searches a particular fault handler from the method call stack for an exception thrown in an unprotected block. If none is found then the .NET runtime generates an UnhandledExceptions event.
Exception Class Hierarchy
Using Try/Catch Blocks
To catch and handle exceptions, you need to place exception-throwing code in a try code and catch the resulting exception when it is thrown, in one or more catch blocks where error handling code is placed to properly deal and recover from the exception. You can optionally add a finally block whose code will always be executed regardless of whether or not an exception is thrown.
C# code
class Program
{
static void Main(string[] args)
{
int[] x = new int[2];
try
{
x[0] = 10;
x[1] = 20;
//SUSPECTED CODE
for (int i = 0; i <= x.Length; i++)
{
Console.WriteLine(x[i]);
}
}
catch (IndexOutOfRangeException err) // Exception handling
{
Console.WriteLine(err.Message);
}
Console.ReadKey();
}
}
Here in this program we declare an integer type of array x with size 2, assign some values 10, 20 to each array element and display these values using a for loop. The for loop is started from 0 and executed thrice due to specification of the equal sign in the condition. But the array size is 2 and the loop is executed three times so the .Net runtime immediately throws an IndexOutOfRange error which is filtered and handled by the catch block. The program output with the error display is as in the following:
Figure 1.1: Index Out of Range Error
NOTE : If you don't provide a specific error, for instance IndexOutOfRange and only specify a default Exception class in the catch block then the .NET runtime handles this by filtering it to an "out of index" error as in the following.
C# code
try
{
...
}
catch (Exception err) // Exception handling
{
Console.WriteLine(err.Message);
}
Using Multiple Catch Blocks
If your code can potentially throw several types of exceptions, you can add multiple catch blocks, one for each exception type. When using multiple catch blocks, catch the most specific exceptions first and catch the most general exception last.
C# code
public static void Main(string[] args)
{
try
{
int total = 0;
for (int i = 0; i < args.Length; i++)
{
total += Int32.Parse(args[i]);
}
Console.WriteLine("you entered {0} arguments and their sum is {1} ",args.Length,total);
// Exception Handling
catch (FormatException)
{
Console.WriteLine("Can't convert to Integer");
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Command Line Arguments is Not Entered");
}
catch (Exception)
{
Console.WriteLine("Unknown Error");
}
Console.ReadLine();
}
This program has multiple catch blocks and converts command-line arguments to integer, adds them, and prints the sum to the console.
Figure 1.2: Multiple catch statements
Using a Finally Block
In certain programming circumstances, you will want to ensure that a certain piece of code executed in all cases, regardless of whether or not an exception is thrown. A finally block is used for just this purpose.
C# code
public static void Main(string[] args)
{
try
{
if (args.Length > 0)
{
Console.WriteLine(args[0]);
}
}
// Exception Handling
catch (IndexOutOfRangeException)
{
Console.WriteLine("Index out of Range");
}
finally
{
Console.WriteLine("Thank you for Using Exception Handling!");
}
Console.ReadLine();
}
When you compile this program, no matter what happened when the program runs, the "Thank you…" message is printed to the console as in the following:
Figure 1.3: Finally block
Custom Exception class
In most cases, the .NET built-in exception classes, combined with custom messages, should meet your exception handling requirements. However, in some cases, you might need exception types that are specific to the problem you are solving. Microsoft recommends that you consider the following when you design a custom exception class:
- Create an exception class only if no exception class exists that satisfies your requirement.
- Derive all custom exception classes from the ApplicationException or Exception class.
- Implement the constructor with the signature shown as:
C# code
public class NumberZeroExceptionMagic : ApplicationException
{
public NumberZeroExceptionMagic() : base() { }
// The constructor that takes msg as a parameter
public NumberZeroExceptionMagic(string msg) : base(msg) { }
}
The following example divides 2 integer type numbers but if we divide the denominator by Zero then the .NET runtime throws DivideByZeroException. So that situation can also be handled by a Custom Exception class as in the following:
C# code
public class NumberZeroExceptionMagic : ApplicationException
{
public NumberZeroExceptionMagic() : base() { }
// The constructor that takes msg as a parameter
public NumberZeroExceptionMagic(string msg) : base(msg) { }
}
public class Test
{
static void Main(string[] args)
{
// Prompt the user to enter an integer
Console.WriteLine("Please input an integer: ");
// Try this block of code
try
{
int Numerator = 10;
// Store the input in x
int x = Int32.Parse(Console.ReadLine());
// If the number entered is 0...
if (x == 0)
{
// ...throw our custom exception with the following message parameter
throw new NumberZeroExceptionMagic("You are not allowed to enter number 0");
}
Console.WriteLine("Division: " + (Numerator / x).ToString());
}
// Catch the custom exception
catch (NumberZeroExceptionMagic e)
{
// And display the error message
Console.WriteLine("Exception: " + e.Message);
}
Console.ReadLine();
}
}
This program takes as input a denominator from the user. When the user enters any number except zero, the program shows the desired output but if the user enters zero then the exception is thrown by the custom exception class as in the following:
Figure 1.4: Custom Exception class