Exception Handlers in C#

In this article, I discuss what Exception Handlers are in C# and how to trap errors using try/catch blocks and their behavior when we use multiple catch statements to handle errors and finaly, how to create User-Defined exceptions.
Exception Handling
A C# Exception is an object that describes an exceptional (i.e., Error) condition that has occurred in a piece of code.
When an exception condition occurs, an object representing the exception is created and thrown in the method that caused the error. That method can handle the exception itself, or pass it on. Either way, at some point, the exception is caught and processed.
An exception can be generated at run-time or they can be manually generated by your code.
C# Exception Handling
In C# Exception Handling is managed via four keywords:
  1. try
  2. catch
  3. finally
  4. throw
Briefly, how they work is described here.
The piece of code that you want to monitor for exception is enclosed with in a try block. The try block can be followed by one or more (multiple) catch blocks. If an exception occurs within a try block it is thrown.
Your code can catch an exception using catch and handle it. The program can have one or more catch sections following each try block.
We can write multiple catch statements to handle various type of exceptions if the try block potentially raises more than one type of exception.
Any code that must absolutely be executed, whether an exception is thrown or not, is enclosed within the finally block.
For example: If a method opens a file, it must be closed whether an exception is raised or not.
To manually throw an exception, we use the keyword throw.
General Form of Exception handling block
The syntax for using try/catch looks like the following:
  1. try  
  2.  {  
  3.      //Piece of code to Monitor for error  
  4.  }  
  5.  catch (ExceptionType1 exObj1)  
  6.  {  
  7.      //Handler for ExceptionType1  
  8.  }  
  9.  catch (ExceptionType2 exObj2)  
  10.  {  
  11.      //Handler for ExceptionType2  
  12.  }  
  13.  finally  
  14.  {  
  15.      //block of code to be executed before try block ends  
  16.  }  
We can write multiple catch statements to handle various types of exceptions in case our try block raises more than one exception in various situations.
Exception Classes in C#
Exceptions in C# are represented by classes. Exception classes in C# are primarily directly or indirectly derived from the System.Exception class. 
Some of the exception classes derived from the System.Exception class are:
  1. System.ApplicationException
  2. System.SystemException
The System.ApplicationException class supports exceptions that are generated by the Application programs. The exceptions defined by the programmers should be derived from this class.
Let me explain this to you in the User-Defined exception section.
The System.SystemException class is the base class for all Predefined/System built-in exceptions. Some of the Predefined exceptions are:
Exception Class Description
System.IO.IOException To Handle I/O Errors
System.IndexOutOfRangeException To Handle errors generated when a method refers to index out of range
System.DivideByZeroException To Handle error generated when dividing a number by zero.
To Handle errors generated during type-casting(Converting one type to another)
Uncaught Exceptions
Before we begin to explain how to handle exceptions, let us see what happens if you don't handle them. Therefore, as an example, we try to divide a number by zero. The following code intentionally causes a divide by zero error:
  1. int a = 0;  
  2. int b = 77 / a;  
Here is the output generated when the program is executed:
When this program is executed, a Systems predefined exception is raised and thrown.
The execution of the program stops, because once an exception has been thrown, it must be caught by an Exception handler (usually the catch block). In this piece of code above we have not supplied any exception handler.
Handling Exceptions (Using Try & Catch)
The error handling blocks are implemented using try, catch, & finally keywords. The following is an example of how to handle the exception when a divide by zero condition occurs.
Handling an exception provides two benefits:
  1. It allows you to fix error.
  2. Prevents program from terminating automatically.
To handle a run-time error, simply enclose the code that you want to monitor inside a try block, following immediately by the catch clause that specifies the exception type that you wish to catch.
To illustrate how this can be done, the following program includes a try block and catch clause that processes the DivideByZeroException generated by the division by zero error.
  1. try  
  2. {  
  3.     int a = 0;  
  4.     int b = 77 / a; // Once executing this control is transferred to the catch block  
  5.     MessageBox.Show("After Executing.."); // This Statement is not executed  
  6. }  
  7. catch (DivideByZeroException ex)  
  8. {  
  9.     MessageBox.Show(ex.Message);  
  10. }  
When the program is executed, we will have a message displayed "Attempted to divide by zero".
When the control reaches the "int b = 77 / a" it raises the exception and is thrown. The catch handler traps this exception and the message "Attempted to divide by zero" is displayed.
Multiple catch Clauses
In some cases, more than one exception could be raise by a single piece of code. To handle this type of situation, you can specify two or more catch clauses, each having a different type to handle various exceptions.
When an exception is thrown, each catch is inspected in order and the first one whose type matches that of the exception is executed. After one catch statement executes, the others are bypassed and execution continues after the try/catch block.
To illustrate how this is done we will look at the following program:
  1. try  
  2.   {  
  3.       int a = 2;  
  4.       int b = 77 / a;  
  5.       int[] arr = {1};  
  6.       arr[27] = 100;  
  7.   }  
  8.   catch (DivideByZeroException ex)  
  9.   {  
  10.       MessageBox.Show(ex.Message);  
  11.   }  
  12.   catch (IndexOutOfRangeException ex1)  
  13.   {  
  14.       MessageBox.Show(ex1.Message);  
  15.   }  
The program causes a divide by zero exception if a is assigned a value 0.
It will survive the division if you assign a to something larger than zero.
But, the program above will cause an IndexOutOfRangeException since the int array arr has a length of 1, yet we attempt to assign a value to c[27].
The following program contains an error:
  1. /* A subclass must come before it Superclass in a series 
  2. * of catch statments. If not complile-error will result. 
  3. */  
  4. try  
  5.  {  
  6.      int a = 0;  
  7.      int b = 77 / a;  
  8.  }  
  9.  catch (SystemException ex)  
  10.  {  
  11.      MessageBox.Show(ex.Message);  
  12.  }  
  13.  catch (DivideByZeroException ex)  
  14.  {  
  15.      MessageBox.Show(ex.Message);  
  16.  }  
If you try to compile this program, you will receive an error message stating that a previous catch clause already catches all exceptions of this or of a super type ("System.SystemException").
Syntax notification Squiggles underlines code that won't code or that might cause an error before we compile the project here (DivideByZeroException).
Since DivideByZeroException is a subclass of SystemException, the first catch statement will handle all Exception-based errors including DivideByZeroException. This means the second catch staement will never execute.
To fix the problem reverse the order of catch statements as:
  1. try  
  2.   {  
  3.       int a = 0;  
  4.       int b = 77 / a;  
  5.   }  
  6.   catch (DivideByZeroException ex)  
  7.   {  
  8.       MessageBox.Show(ex.Message);  
  9.   }  
  10.   catch (SystemException ex)  
  11.   {  
  12.       MessageBox.Show(ex.Message);  
  13.   }  
When you use multiple catch statements, it is important to remember that the exception subclass must come before any of their super classes. This is because a catch statement that uses a super class will catch exceptions of that type plus any of it subclasses.
Thus, a subclass would never be reached if it comes after its superclass.
User-Defined Exceptions
We have only been catching exceptions that are thrown so far. However, it is also possible to define your program to throw an exception explicitly.
The general form of throwing a User-Defined exception is:
throw UserDefinedInstance;
Here, the UserDefinedInstance must be an object of the UserDefinedException type or a subclass of it.
User-Defined Exception classes are derived from the ApplicationException class.
Here is an example to illustrate how to create User-Defined exceptions:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Linq;  
  7. using System.Text;  
  8. using System.Windows.Forms;  
  9. namespace ErrorHandling  
  10. {  
  11.     public partial class frmErrorHandler : Form  
  12.     {  
  13.         int a = 0;  
  14.         public frmErrorHandler()  
  15.         {  
  16.             InitializeComponent();  
  17.         }  
  18.         private void frmErrorHandler_Load(object sender, EventArgs e)  
  19.         {  
  20.             try  
  21.             {  
  22.                 if (a == 0)  
  23.                 {  
  24.                     throw (new ValueisZeroException("Cannot be assigned value Zero"));  
  25.                 }  
  26.             }  
  27.             catch (ValueisZeroException ex)  
  28.             {  
  29.                 MessageBox.Show(ex.Message);  
  30.             }  
  31.         }  
  32.         public class ValueisZeroException : ApplicationException  
  33.         {  
  34.             public ValueisZeroException(string txt) : base(txt)  
  35.             {  
  36.             }  
  37.         }  
  38.     }  
  39. }  
In this article I have discussed what Exception Handlers are in C# and how to trap errors using try/catch blocks and their behavior when we use multiple catch statements to handle errors and finaly, how to create User-Defined exceptions.