Exception handling in C# (with examples)

Exception handling is a must-use tool for programmers. It avoids the uncomfortable app crashes that we see often when using certain software.

In this post, you will learn what an exception is and how to handle them in C#.

1- What is an Exception?

An exception in a programming language is an unexpected event that stops the current flow execution of a program.

Some examples of exceptions are:

  • The (in) famous blue screen in Microsoft Windows.
  • When you are using an app on your phone, the app just closes without any further notice.
  • On your computer, an application closes, and the operating system shows you a crash report window.

In short, when an exception occurs, the user won’t be able to keep using the application at that moment (until the app is restarted). This can cause loss of information, inconsistency in the data stored, etc.

So, as a software developer (or programmer), you should make sure that your application doesn’t crash, or at least, that it “crashes gracefully”.

For instance, if you are taking the Google Associate Android Developer certification exam, you must submit an app. If this app crashes, you are immediately disqualified. That’s how important it is this topic for a developer.

2- What is Exception Handling?

Exception handling is the way that software developers deal with exceptions. It is what every programmer must do to make sure that the application won’t crash.

We can have two types of exceptions: the type that we can plan how to recover, and the type that the application will definitely crash.

Examples of the second type are:

  • If the computer memory is full. Apps need memory to be executed. If there is not enough memory, the app will crash and there is nothing we can do about it. Except, to make sure that there is enough memory before the app is executed.
  • The operating system fails. In this case, not only your app will crash, but the whole operating system. A typical error when this situation can happen is in UNIX-based systems when you get the error “Kernel Panic“.
  • Among others.

There are some types of exceptions that you can plan beforehand on how to recover:

  • Wrong user input. This one happens when you ask for a number, and you get a string. If you don’t handle this type of situation, your program will eventually crash when a user enters the wrong type of input.
  • When you are doing divisions. As you should know from mathematics, division by 0 is not defined. If you try to divide by 0 in your app, it will crash. Let’s say you are creating a calculator; you must consider this situation.
  • You want to save data to a file. If you (the computer user), don’t have writing permissions in the folder/file you want to save the data to, an exception will occur. Here, if you don’t handle the exception your app will crash. A possible way of handling this type of exception is to tell the user he/she does not have enough permission to write to that file/folder, and you give the option of choosing another location/file.

By now, you should have a clear idea of what exceptions are and why they have to be handled by the software developer.

Let’s see now what tools we have available to implement exception handling in Java.

3- Exception handling in C#

In C#, as in any other programming language, we have three type blocks to handle exceptions.

  • try: within this block, you should write the instructions that can create an exception. Like a division instruction.
  • catch: within this block, you will write the code you want to be executed in the case that an exception happens. If there is a division by 0 exception, in this section you can write a code that will tell the user division by 0 is undefined and shows a prompt asking for a different number.
  • finally: the code within this block, will always be executed, whether an exception happens or not. This block is useful to do compulsory steps, like closing a file, deleting temporary files, etc.

Let’s see the C# code for the division example without exception handling.

using System;

namespace ExceptionHandling
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.Write("Enter the dividend: ");
            int dividend = int.Parse( Console.ReadLine()) ;
            Console.Write("Enter the divisor: ");
            int divisor = int.Parse(Console.ReadLine());
            float result = (float) dividend/divisor;
            Console.WriteLine("The result is: " + result);
        }
    }
}

If you execute this code, with inputs 10 and 5, you will get the following result.

division output example 1
Division output example 1

If the user enters the value 0 as the divisor, then an exception will occur, as in the image below.

DivideByZeroException exception in C#
DivideByZeroException exception in C#

If you add exception handling to your code, it will look like this:

using System;

namespace ExceptionHandling
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.Write("Enter the dividend: ");
            int dividend = int.Parse( Console.ReadLine()) ;
            Console.Write("Enter the divisor: ");
            int divisor = int.Parse(Console.ReadLine());
            try
            {
                float result = dividend / divisor;
                Console.WriteLine("The result is: " + result);
            }
            catch (DivideByZeroException ex) {
                Console.WriteLine("Division by 0 is undefined");
            }
        }
    }
}

In this case, if the user enters the value 0 as the divisor, it will get the result below.

division by 0 output example in c# with exception handling
Division by 0 output example in c# with exception handling

In this case, the app will still close. However, the user of the app will know what went wrong.

Notice that a user of a computer usually doesn’t know what an exception is. Also, he or she won’t understand the error message.

That’s one of the reasons why it is important to handle exceptions. So, the user knows what went wrong and can correct the input to obtain the desired output.

In our previous example, we can also use a loop to keep asking the user to enter a divisor different from 0. That is a better way because the user won’t have to open again the application to keep using it.

Also, remember there are different ways to solve the same problem, this is especially true in programming. In this example, you can use a conditional statement to avoid the app crash. But you will find cases in which you won’t be able to do it like that. Such a case is when you are working with files.

4- Exception Classes Hierarchy

From the code example in the previous section, you can see this line of code:

"catch (Exception ex){"

This means that the exception we are planning to catch will be stored in a variable named ex, and the type of that variable is Exception.

“Exception” is a C# class that will give us access to details about the exception that occurred. But this one is not the only class.

Find below the exception-related class hierarchy.

Example of exception hierarchy in C#
Example of exception hierarchy in C#. Source.

As you can see, we have different types of exceptions. Each class that defines a certain type of exception is part of a hierarchy.

How can we use this?

There are cases in which more than an exception can occur. 

Because we have to give precise information to the users on what happens while using the software, it is always recommended that the error message you show to the user is as specific as possible.

In our previous example of the division, two exceptions can occur ArithmeticException (division by 0) and InputMistmatchException (if you enter a string instead of a number).

What to do in this case?

Programming languages give you the possibility of having as many catch blocks as you need. In this case, we can use one catch block for each of the two exceptions we identified, and an extra one in case another exception happens. See the code below.

using System;

namespace ExceptionHandling
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.Write("Enter the dividend: ");
                int dividend = int.Parse(Console.ReadLine());
                Console.Write("Enter the divisor: ");
                int divisor = int.Parse(Console.ReadLine());
                float result = dividend / divisor;
                Console.WriteLine("The result is: " + result);
            }
            catch (System.FormatException)
            {
                Console.WriteLine("Please enter a number.");
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("Division by 0 is undefined");
            }
            catch (Exception)
            {
                Console.WriteLine("An unexpected error occurred.");
            }
        }
    }
}

Notice that the class at top of the hierarchy (Exception) goes last.

When capturing several exception types, the hierarchy matters. If we use the class Exception in the first catch block, the other exceptions will never be caught. Because all the other exception-related classes inherit from the class Exception.

In other words, we always must catch first the most specific exception, and move down to the more general ones.

Also, remember that the code that can throw an exception, must be within the try block. That’s why in this case, we add to the try block the lines that expect the user to enter an integer value.

5- How and when to throw an exception

As programmers, sometimes we will have to create libraries for a specific purpose.

As part of these libraries, we can create our own exception types.

Let’s say we are creating a library that will implement classes that will facilitate the work with collections.

For the sake of example, let’s assume that our collections will have a maximum of 1000 elements. For instance, a list of students, cities, etc.

Because Exceptions are a topic that all programmers use, we can also use them as part of our implementation. Let’s say that when a user of our class list is trying to add an element, and there are 1000 elements already (the maximum allowed), an exception will be thrown.

For us to implement that, the first step is to create our own type of exception because this one does not match with the ones defined in the programming language.

The way we can do it is by creating a new class that will inherit from the class Exception. We just have to give a name to the class that will indicate the type of exception.

class MaximumExceededException: Exception
    {
        public MaximumExceededException(string message) : base(message)
        {
            
        }
    }

Now, let’s implement the method addElement method.

namespace ExceptionHandling
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Enter the dividend: ");
            int dividend = int.Parse(Console.ReadLine());
            if (dividend > 10)
                throw new MaximumExceededException("The number should be lower than 10.");
        }
   }
}

When to throw a self-defined exception?

From the code above, we can see that if the dividend is greater than 10, then we throw the exception ‘MaximumExceededException’.

Notice, that this maximum (10) is defined by us, because we are implementing the class and, in our environment, the maximum dividend is 10. However, for someone else, it can be different. So, you must follow the considerations related to the problem you are solving.

Summary

To handle exceptions, we use a (or several, as many as we need) block try-catch-finally. The block finally is not compulsory, you use it if you need to do something regardless of whether an exception occurred or not.

Within the catch block, you always must give meaningful information to the user. Remember if the user does not have programming training (most of them don’t), they won’t understand a message ArgumentNullException. If the user gets the same error every time, the result will be your app will be deleted, and we don’t want that to happen.

When using several catch blocks, you need to consider the level of each type of exception in the exception hierarchy. You should start with the class that is lower in the hierarchy and finish with the class that is highest in the hierarchy (the class Exception).

H@ppy coding!