Exceptional Handling (C#)

mmweerarathna
9 min readSep 30, 2023

--

There are several types of errors that can happen in a program because of the mistakes made by the developers who develop the software, and some of the errors that can happen in a system can be named semantic errors (logical errors happen in the system), syntax errors (errors in the syntax of coding or program language), and most importantly, runtime errors (errors happen at the runtime of the system). From the above-mentioned errors, syntax errors can be identified at compile time, and it’s easy to fix them because the IDE usually gives the correct syntax needed. However, semantic and runtime errors can only be identified at runtime, and sometimes the developer needs to debug the system to identify the cause of the error.

In this article, we talk about what runtime errors are, what causes runtime errors in a system, and what we can do to fix those runtime errors, so the system won’t crash.

Mainly runtime errors occur at runtime, and they cannot be identified at compile time, as mentioned in the above paragraph, and those can be identified only at runtime. And it’s important that the developers manage those runtime errors because those runtime errors can cause the system to crash and stop. When a runtime error occurs in the system, we call it as system throws an exception, not like system gives an error. That’s because runtime errors cause exceptional errors in the system, and there are a number of exceptional errors that can happen in a system (some of the main exceptional errors are described at the bottom of the article). Unlike syntax and semantic errors, we cannot fix exceptional errors (runtime errors). What we can do is only manage those exceptional errors from crashing the system, and that’s known as exceptional handling in software development and that’s done by a special way in a system. Let’s learn how we can handle those exceptional errors that can happen in a system.

How to use exceptional handling method to handle exceptional errors.

To get an idea of what these exceptional errors are, first we create a class called Numbers and create an integer array that contains five integer values. We then create a function to get the value of a particular element in the integer array. The function in the Numbers class gets the element number as a user input and returns the value of the particular element.

Within the main function, we create an object for the Numbers class, and in the next line, we call the GetNumber function with 1 as the parameter. So, this function returns the value of the first element in the numbers array. This works fine without giving an error because there is an element in the first index. But in the next line, we call the same function by passing 10 as the parameter. This will throw an exception in the system in the GetNumber function (because that function tries to get the value of the 10th element in the array), because the array contains only 5 elements, so it creates an index bound of range exception in the system and stops the execution of the system and crashes the application. Error messages thrown into the system are shown in the image-1.

//Example one
namespace LearnExceptionalHandling
{
//Create public class named as numbers
public class Numbers
{
//Create array that contains 5 int values
int[] numbers = { 10, 20, 30, 40, 50 };

//Function to return a single number from the array
public int GetNumber(int element)
{
return numbers[element];
}
}

class Program
{
static void Main(string[] args)
{
//Create an object for Numbers class
Numbers numbers = new Numbers();

//Call the GetNumber function
int value = numbers.GetNumber(1); //** works fine
int value = numbers.GetNumber(10); //** exceptional error occur

//Print the output from the function GetNumber
Console.WriteLine("Value = {0}", value);
Console.ReadLine();
}
}
}
image-1

Let’s identify these two scenarios this way: Imagine if we took the user input value instead of a hard-coded value in the GetNumber function. If the user enters a value from 1 to 5, the system won’t crash and will work perfectly, but if the user enters a number higher than 5, the system will crash, throw an exceptional error, and stop the execution. That’s an exceptional error that we need to manage in the system as developers. The below example shows how we can manage the exceptional errors that happen in the system.

We use a special mechanism to handle the exceptional errors that can happen in a system. That is, we use two blocks in the code to handle those exceptional errors in a system, and those two blocks are known as try-and-catch blocks (that code block is shown in the code section below). Within these two blocks, we put totally different codes. Within the try block, we put the code lines that we think might throw a runtime error when the system is up and running, and within the catch block, we put the code lines that say what we should do when an exceptional error occurs in the code lines within the try block. This mechanism is well explained in the following paragraph with an example.

Using Try and Catch block.

namespace LearnExceptionalHandling
{
//Create public class named as numbers
public class Numbers
{
//Create array that contains 5 int values
int[] numbers = { 10, 20, 30, 40, 50 };

//Function to get a number from the array
public int GetNumber(int element)
{
try
{
//Try block contains the code that we think
//an exception may occur in runtime
return numbers[element];
}
catch (Exception ex)
{
//Catch block catches the runtime error happens in
//the try block and do whatever the developer wants
//to do with the error

//Output a custom error message to the console
Console.WriteLine("There is no element at {0} place.", element);
//Output the exception message to the console
Console.WriteLine(ex);
Console.ReadLine();
throw;
}
}
}

class Program
{
static void Main(string[] args)
{
//Create an object for Numbers class
Numbers numbers = new Numbers();

//Call the GetNumber function
int value = numbers.GetNumber(10);

//Print the output from the function GetNumber
Console.WriteLine("Value = {0}", value);
Console.ReadLine();
}
}
}
image-2

As mentioned above, we know that there is a chance to get an exceptional error in the code when we try to get an element from the numbers array in the Number class (because the user may enter a number greater than five). So, we need to manage that exceptional error in our codebase. In order to manage that exceptional error, we put a try-and-catch block as shown in the above code block.

The Try block contains the code lines that we suspect might throw an error in the system, while the Catch block contains the code line for what to do when an exceptional error occurs in the try block above.

In the above code block, we add a try and catch block in the GetNumber function. We put the line that gets the element from the array in the try block (because that is the line where an exceptional error happens), and in the catch block, we output an error message to the console and throw the error. So now if a user enters a value greater than 5, the system will catch the exceptional error, output an error message to the user without crashing the system entirely, and continue the execution of the system (we can output the exceptional error message to the console if we need to). (Explained in the code block above.) Sometimes we can log the error to a file while outputting a message to the user. That logging function is also written in the catch block.

When we use the Try and Catch block, we can output an error message to the user saying what went wrong without shutting down the entire system and confusing the user. In the first case, the user has no idea what went wrong in the system, but in the second scenario, the user knows what went wrong in the system. The image -2 shows the output error message in the console with the exception error details.

Using Finally Block

Imagine a scenario like this: we need to empty the array when a function runs; basically, that needs to be done after outputting the value of the particular element to the console. We can successfully complete this mechanism when no exceptional error occurs in the try block. That’s because it executes the next line in the try block successfully.

public class Numbers
{
int[] numbers = { 10, 20, 30, 40, 50 };

public int GetNumber()
{
try
{
//This line throws an exception
var value = numbers[10];

//Because of the exception this line wont run
//and wont clear the array
Array.Clear(numbers, 0, numbers.Length);
return value;
}
catch (Exception ex)
{
Console.WriteLine("There is no element at {0} place.", 10);

Console.WriteLine(ex);
Console.ReadLine();
throw;
}
}
}

But if an exceptional error occurs in the try block, it immediately jumps to the catch block, leaving the next line behind. In this scenario, we wouldn’t be able to clear the number array. So, there is a special method that developers follow to prevent this issue from happening when they use try and catch blocks; that’s functionality is explained in the below paragraph.

In the above-mentioned scenario, we need to clear the number array no matter what happens in the try block, whether code lines in the try block throw an exception or not. There is another block that comes after this try-and-catch block to do that special scenario, which means that by using that special block, we can operate this particular scenario. That block is known as the final block. Whatever function we put inside that finally block, it runs no matter what happens in the try block.

The function in the finally block runs no matter what happens in the try block, as explained in the above paragraph. It runs whether the block throws an error or not.

Using Multiple catch blocks

Examples shown above only contain a single catch block in their function and return the same message for every exception. Whether the error is null reference exception system outputs the error message ”There is no element at 10". Whether it is an index-out-of range exception, the system outputs the error message “There is no element at 10.”. It catches the exception error but outputs a different error message to the console. To prevent this issue, we can use multiple catch blocks after the try block in the function, so we can output different error messages and the correct error message to the user.

There are multiple catch blocks created in the following code. The system will run the try section first, and if there is an exceptional error in the try block, it will check whether there are any matching blocks for the exceptional error and run the code inside the block if there are any; otherwise, it will run the last catch block in the function.

public class Numbers
{
int[] numbers = { 10, 20, 30, 40, 50 };

public int GetNumber(int element)
{
try
{
return numbers[element];
}
catch (NullReferenceException ex)
{
//This catch block executes when there is an
//nullreference exception
Console.WriteLine("element is null.", element);
Console.WriteLine(ex);
Console.ReadLine();
throw;
}
catch(IndexOutOfRangeException ex)
{
//This catch block executes when there is an
//indexoutofrange exception
Console.WriteLine("array contains only 5 elements.", element);
Console.WriteLine(ex);
Console.ReadLine();
throw;
}
catch(Exception ex)
{
//This catch block executes when there is an other exception error
//other than nullreference exception or indexoutofrange exception
Console.WriteLine("Error happens when we try to get an element.", element);
Console.WriteLine(ex);
Console.ReadLine();
throw;
}
finally
{
Array.Clear(numbers, 0, numbers.Length);
Console.WriteLine(numbers);
}
}
}

This way, we can customize what should happen inside the system for different exceptional errors, and we can output different error messages to the user depending on the exceptional error that happens within the try block. Then it runs the finally block.

Types of exceptional errors that can happen in a system.

Null Reference exception.

This exception error occurs when we try to access a member of a type with no value (a null value). This null reference exception can be managed using exceptional handling.

Division by zero exception.

This exception error occurs when we try to divide an integer value or a decimal value with zero. This division by zero exception can also be managed using the exceptional handling method.

Value overflow exception.

This exception error occurs when we try to use a value outside the bound. This exceptional error, too, can be managed with an exceptional handling method.

--

--