The objective of this article is to understand the concept of exception handling in C# programming language. An exception is an error that occurs at runtime of the program's execution. These runtime errors are unexpected situations that interrupt the normal flow of the program/application. For example: divide by zero exception, file not found exception, class not found exception, etc. So, this article will help you to understand how to deal with such unexpected situations using the exception handling.
Introduction to C# Exception Handling
Exception handling is the feature that provides a way to transfer control from one block of code to another when an unexpected error/situation arises. Every time we can't check where a program threw an error, hence exception handling comes into the picture. Through this feature, we can avoid the unexpected termination of a program. The try
block monitors the exception that may occur at runtime, and if the exception occurs, then it is raised/thrown. The catch
block handles the exception, which is raised/thrown from within the try
block. We can't have only a catch
block without a try
block, which means both blocks work together to handle the exception. The try
block must be followed by a single or the multiple catch
block(s). We can also have a nested try
and catch
blocks.
The Anatomy of C# Exceptions
-
try
- A try
block is used to contain a piece of code from which the outcome of an exception is possible. If an exception occurs in the try
block code, then the exception will be handled by the corresponding catch
block.
-
catch
- A catch
block is used to handle an exception thrown from the try
block.
-
finally
- The finally
block is used to execute certain instructions, whether an exception is thrown or not. It is useful when one wants to close the database connection, file stream, etc. that were opened inside the try
block.
-
throw
- The throw
keyword is used to create a new exception. It can be used in the catch
block to re-throw an exception handled in the catch
block.
The syntax for exception handling is shown here:
try
{
// statements due to which exception may arise
}
catch()
{
// exception handling code
}
catch()
{
// exception handling code
}
finally
{
// finally block statements always get executed
// this is to perform some final task like DB connection close etc.
}
Let's take an example of divide by zero which leads to an exception,
Filename: Program.cs
using System;
namespace Studytonight
{
public class Program
{
public static void Main(string[] args)
{
int num1 = 10;
int num2 = 0;
int result = num1/num2;
Console.WriteLine(result);
Console.WriteLine("Execution terminated at previous line");
}
}
}
Output:
Run-time exception (line 11): Attempted to divide by zero.
Stack Trace:
[System.DivideByZeroException: Attempted to divide by zero.]
at Studytonight.Program.Main(String[] args) :line 11
In the above example, we tried to divide a number by zero and faced the unhandled divide by zero exception. In the next example, we will see how to handle such kind of runtime exception using the try
and catch
block.
List of Exceptions and Overview
The C# exceptions are represented by classes. All the exception classes must be derived from System.Exception
class where System is a namespace and Exception
is built-in exception class. Exceptions can be raised manually/explicitly by using the throw
keyword and can be generated by the .NET Framework, by the common language runtime (CLR), or by application code. Following is the list of exceptions that are commonly seen.
Exception |
Description |
ArgumentException |
The exception occurs when invalid arguments provided to a method |
ArgumentNullException |
The exception occurs when a method requires the argument and no argument provided |
FormatException |
The exception occurs when the format of an argument is invalid |
FileNotFoundException |
This exception occurs when access to a file provided and file is not present in the disk or at the specified path |
IOException |
The exception is thrown when an input/output error occurs |
IndexOutOfRangeException |
The exception occurs when the elements of an array or collection with an index outside its bounds |
DivideByZeroException |
The exception occurs when a number divide by zero |
SystemException |
This is the base class for system exceptions namespace |
Time for Examples!
Let's take an example of divide by zero with try
and catch
blocks
Filename: Program.cs
using System;
namespace Studytonight
{
public class Program
{
public static void Main(string[] args)
{
try
{
int num1 = 10;
int num2 = 0;
int result = num1/num2;
Console.WriteLine(result);
}
// catch the DivideByZeroException exception
catch(DivideByZeroException)
{
Console.WriteLine("Divide by zero exception");
}
// catch all other exceptions
catch(Exception e)
{
Console.WriteLine("Exception caught: "+e);
}
}
}
}
Output:
Divide by zero exception
In the above example, we have used a single try
block with multiple catch
blocks. If you closely observe the first catch
block, in it we specified the known exception which got handled. Hence, the second catch
block didn't got executed. If we don't know which exceptions are going to occur, then we specify an object of Exception type in the catch
clause and use that object to recognize which exception was occurred, because all the exception classes in C# are derived from the Exception class.
Let's take an example with nested try
and catch
and finally
block.
Filename: Program.cs
using System;
namespace Studytonight
{
public class Program
{
public static void Main(string[] args)
{
try
{
try
{
int num1 = 10;
int num2 = 0;
int result = num1/num2;
Console.WriteLine(result);
}
catch(DivideByZeroException e)
{
Console.WriteLine(e.Message);
}
int[] arr = {10,20,30};
Console.WriteLine(arr[10]);
}
catch(Exception e)
{
Console.WriteLine("Exception caught: "+e);
}
finally
{
Console.WriteLine("Finally block executed");
}
}
}
}
Output:
Attempted to divide by zero.
Exception caught: System.IndexOutOfRangeException: Index was outside the bounds of the array.
Finally block executed
In the above example, we have used nested try
and catch
blocks. When the divide by zero exception occurs in the try
block, then the corresponding catch
block gets executed and the Message
property describes the current exception. Thus, the message attempted to divide by zero is printed on the console. On the other side, when we don't know which exception is going to occur, then create an object of exception type in catch
clause and use that object to recognize which exception occurred. In our case, we also get the index is out of array bounds exception caused due to arr[10]
, because only three elements are present in the array. At last, we have used the finally
block, which is always executed whether an exception is thrown or not.
Let's take a simple example for throw
keyword.
Filename: Program.cs
using System;
namespace Studytonight
{
public class Program
{
public static void Main(string[] args)
{
string s = null;
if(string.IsNullOrEmpty(s))
{
throw new NullReferenceException("String is null");
}
}
}
}
Output:
Run-time exception (line 12): String is null
Stack Trace:
[System.NullReferenceException: String is null]
at Studytonight.Program.Main(String[] args) :line 12
In the above example, we have created a new exception using the throw
keyword and we used the new
keyword in the throw
statement to create an object of the valid exception type. The if
block will always execute as the string s
is null.
C# User-Defined Exceptions
The C# programming language allows us to create custom exception classes and to create a user-defined exception class, we need to inherit the Exception class in it. The following example demonstrates how to define a custom exception class by inheriting the Exception class.
Filename: Program.cs
using System;
namespace Studytonight
{
public class CustomException:Exception
{
public CustomException(string s):base(s)
{
// do something
}
}
public class Program
{
public static void Main(string[] args)
{
try
{
string s = null;
if(string.IsNullOrEmpty(s))
{
throw new CustomException("String is null");
}
}
catch(CustomException ce)
{
Console.WriteLine(ce.Message);
}
}
}
}
Output:
String is null
In the above example, we have inherited the Exception class into our user-defined exception class CustomException. As we know, the if
block will always be executed as the string s
is null, and using the throw
statement, we raised our CustomException to throw an exception. Here, the CustomException is a derived class. Hence, the base
keyword is used to access members of the base class from within a derived class.
Conclusion:
We hope this article helped you to understand the exception handling in C# language. If you have any queries, then please let us know in the comment section. We are happy to solve your doubts.