Introduction
Try catch in C# helps catch errors and exceptions in .NET apps The try block holds the suspected code that may get exceptions. When an exception is thrown, the .NET CLR checks the catch block and whether the exception is handled. One try block can have multiple catch blocks. A try-catch statement can have other nested try-catch statements.
Try catch in C#
In C#, the try catch statement is responsible for exception handling. The suspect code is placed inside the try block, and the catch declares an exception. A typical try..catch block looks like Listing 1.
try
{
// Suspect code
}
catch (Exception e)
{
// Action after the exception is caught
}
Listing 1
Let's look at the code in Listing 2, which divides an int with 0. First, we put the suspected code within the try block and catch the exception and generate a human-readable error message in the catch block.
try
{
int i = 20;
// Suspect code
int result = i / 0;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Attempted divide by zero. {0}", ex.Message);
}
Listing 2
The code in Listing 2 will throw an exception. In this case, we already know the issue to catch the exception, but the core purpose of exception handling is when we don't know when the error might occur.
Now, let's say you've to use a class written by other developers that has a method, DivideNumer. See Listing 3.
static double DivideNumber(double num1, double num2)
{
if (num1 < num2)
num1 += 20;
return num1 / num2;
}
Listing 3
This is ideal to use a try..catch because we're unsure when an error may occur. See Listing 4
try
{
int i = 20; int j = 0;
double result = DivideNumber(i, j);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Attempted divide by zero. {0}", ex.Message);
}
Listing 4
Multiple catch blocks
C# allows using multiple catch in a try..catch block to catch specific exceptions. Let's take at the code in Listing 5, which may generate Stack overflow, divide by zero, and invalid cast exceptions. We can handle this using separate catch blocks for each exception type.
try
{
string mystring = default;
mystring.Insert(0, "hello");
int i = 20;
int j = 0;
double result = DivideNumber(i, j);
object obj = default;
int i2 = (int)obj;
// Suspect of casting error
}
catch (StackOverflowException ex) {
Console.WriteLine("Overflow. {0}", ex.Message);
}
catch (DivideByZeroException ex) {
Console.WriteLine("Attempted divide by zero. {0}", ex.Message);
}
catch (InvalidCastException ex) {
Console.WriteLine("Invalid casting. {0}", ex.Message);
}
Listing 5
Handle general exceptions
What if we don't know what kind of exception we will get? In that case, we use the Exception class to handle all exceptions. See Listing 6. The Exception block will handle any exceptions not caught by the previous catch blocks.
try
{
string mystring = default;
mystring.Insert(0, "hello");
int i = 20;
int j = 0;
double result = DivideNumber(i, j);
object obj = default;
int i2 = (int)obj;
// Suspect of casting error
}
catch (StackOverflowException ex) {
Console.WriteLine("Overflow. {0}", ex.Message);
}
catch (DivideByZeroException ex) {
Console.WriteLine("Attempted divide by zero. {0}", ex.Message);
}
catch (InvalidCastException ex) {
Console.WriteLine("Invalid casting. {0}", ex.Message);
}
catch (Exception ex) {
Console.WriteLine("General exception. {0}", ex.Message);
}
Listing 6
The try..finally
When an exception occurs in a try block, the program control goes to the catch block if the exception is handled; otherwise, the program execution stops. But often, we require some code to be executed even when an exception occurs. One of the key purposes of the "finally" block is to clean up any resources used in the try block. For example, if an IO resource is used to open and read a file, the IO resource must be released.
The code in Listing 7 creates a FileStream and reads a file that is not there. The code will throw an exception that is not handled. But we want to make sure the FileStream object is released, so we call FileStream.Close() method in the final block.
// Pick a file that is not there
string path = "AFile.txt";
// Create a FileStream
FileStream fs = default;
try
{
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
// Open FileStream
fs = File.Open(path, FileMode.Open);
// Read a file that is not there
while (fs.Read(b, 0, b.Length) > 0) {
Console.WriteLine(temp.GetString(b));
}
}
finally {
Console.WriteLine("Clean up start!");
// Release resources here
fs.Close();
}
Listing 7
The try..catch..finally
The exception handling syntax is try..catch..block that ensures the exceptions are caught and any resources are cleaned up. Listing 8 is an example of using a try..catch..finally block.
// Pick a file that is not there
string path = "AFile.txt";
// Create a FileStream
FileStream fs = default;
try
{
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
// Open FileStream
fs = File.Open(path, FileMode.Open);
// Read a file that is not there
while (fs.Read(b, 0, b.Length) > 0) {
Console.WriteLine(temp.GetString(b));
}
}
catch (FileNotFoundException ex) {
Console.WriteLine("FileNotFound. {0}", ex.Message);
}
finally {
Console.WriteLine("Clean up start!");
// Release resources here
fs.Close();
}
Listing 8
The throw statement
The runtime usually throws exceptions, but C# has the flexibility to throw exceptions manually. A library creator can throw exceptions when certain conditions are unmet, and a caller program can catch the exceptions.
The following syntax throws an exception.
throw exception;
The code in Listing 9 declares a class, MathOps, that contains two methods, FindPrimeNumber and DivideNumber, and both of these methods throw two different exceptions.
using System;
namespace ExceptionThrowSample {
class
MathOps {
// Create an array of numbers
int[] primes = { 2, 3, 5, 7, 11, 13, 17, 19 , 23, 29 };
public int FindPrimeNumber(int index) {
if (index < 0 || index >= primes.Length) {
throw new IndexOutOfRangeException();
}
return primes[index];
}
public double DivideNumber(int num1, int num2) {
// If num2 is 0, throw an exception
if (num2 == 0) throw new DivideByZeroException();
if (num1 < num2) num1 += 20;
return num1 / num2;
}
}
}
Listing 9
Listing 10 is a caller program that calls MathOps class and its methods and catches exceptions using two different catch blocks.
using System;
namespace ExceptionThrowSample {
class
Program {
static void Main(string[] args) {
var ops = new MathOps();
// Create a random number
Random rand = new Random();
int index = rand.Next();
try
{
int prime = ops.FindPrimeNumber(index);
Console.WriteLine($"Number is {prime}");
ops.DivideNumber(prime, index);
}
catch (IndexOutOfRangeException ex) {
Console.WriteLine(ex.Message);
}
catch (DivideByZeroException ex) {
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
Listing 10
Throw expressions (C# 7)
So far, we've seen the throw keyword as a statement, but starting with C# 7, the throw keyword can also be used as an expression. Now, with an expression, the simple condition can be used in the same line, making the code more readable and cleaner.
The code example in Listing 11 throws an exception based on a condition.
public int FindPrimeNumber(int index) {
if (index < 0 || index >= primes.Length) {
throw new IndexOutOfRangeException();
}
return primes[index];
}
Listing 11
Using a throw expression, Listing 11 can be replaced with Listing 12.
public int FindPrimeNumber(int index) {
if (index < 0 || index >= primes.Length) {
throw new IndexOutOfRangeException();
}
return primes[index];
}
Listing 12
Summary
In this article, I discussed using a try-catch statement in C#.
The next recommended articles are Exception Handling in C# and Try Catch Finally in C#.