C# Exception Handling

C# Exception Handling

πŸ’‘
Exception = an unexpected event that occurred during program execution that disrupts the desired flow of the program.

As mentioned above, exceptions disrupt the flow of the program and terminate the program abnormally. It's the developer's responsibility to handle exceptions gracefully. If these errors are not handled properly, the program might crash and you may never know the root cause of failure.

Handling or responding to exceptions is called Exception Handling.

In C#, exception handling is achieved using 4 keywords :

  • try: code that might generate exceptions is put inside the try block.

  • catch: This block 'catches' the exception generated inside try block. The developer could decide what's need done once an exception is generated i.e. logging the stack trace, logging error messages, showing error prompts to the user etc.

  • finally: This code block when written will always execute no matter whether an exception is raised or not. This is useful in scenarios where some clean-up code is required for example closing a file.

  • throw: throw keyword is used to generate new exceptions in code.

In C#, exceptions are a type represented by classes derived from class Exception.

Exception classes

C# .NET includes built-in exception classes for every possible error. The Exception class is the base class of all the exception classes.

below is a list of common C# exception classes :

  • System.NullReferenceException – Very common exception related to calling a method on a null object reference.

  • System.IndexOutOfRangeException – Occurs attempting to access an array element that does not exist.

  • System.IO.IOException – Used around many file I/O type operations.

  • System.NET.WebException – Commonly thrown around any errors performing HTTP calls.

  • System.Data.SqlClient.SqlException – Various types of SQL Server exceptions.

  • System.StackOverflowException – If a method calls itself recursively, you may get this exception.

  • System.OutOfMemoryException – If your app runs out of memory.

  • System.InvalidCastException – If you try to cast an object to a type that it can’t be cast to.

  • System.InvalidOperationException – Common generic exception in a lot of libraries.

  • System.ObjectDisposedException – Trying to use an object that has already been disposed of.

Handling exception

try {
    int num = 0;
    // code that may raise an exception 
    int divideByZero = 7 / num;
}
catch (Exception e){
    // 'catching' and 'handling' the exception
    Console.WriteLine("Exception has occurred :" + e.Message);
}
πŸ’‘
This is a simple example to illustrate exception handling. We could deal with this particular scenario better in practice by checking explicitly for the divisor being zero before operating. Checking for preventable errors is preferable to relying on try/catch blocks because exceptions are relatively expensive to handle, taking hundreds of clock cycles or more

running the above program shows the below output

if finally block is also available then it also gets executed

try {
    int num = 0;
    // code that may raise an exception 
    int divideByZero = 7 / num;
}
catch (Exception e){
    // 'catching' and 'handling' the exception
    Console.WriteLine("Exception has occurred :" + e.Message);
}
finally {
    Console.WriteLine("Inside finally");
}

How exception handling workflow works

When an exception is thrown within a try statement, the CLR performs a test:

Does the try statement have any compatible catch blocks?

β€’ If so, execution jumps to the compatible catch block, followed by the finally block (if present), and then execution continues normally.

β€’ If not, execution jumps directly to the finally block (if present), then the CLR looks up the call stack for other try blocks; if found, it repeats the test.

If no function in the call stack takes responsibility for the exception, the program terminates.

Multiple Catch Blocks

A program code that is written inside a try block might raise multiple types of exceptions. These multiple types of exceptions could be handled using multiple catch blocks. Remember that a more specialized catch block should come before a generalized one. Otherwise, the compiler will show a compilation error.

    class Test {
        static void Main(string[] args) {
            try {
                byte b = byte.Parse(args[0]);
                Console.WriteLine(b);
            }
            catch (IndexOutOfRangeException) {
                Console.WriteLine("Please provide at least one argument");
            }
            catch (FormatException) {
                Console.WriteLine("That's not a number!");
            }
            catch (OverflowException) {
                Console.WriteLine("You've given me more than a byte!");
            }
        }
    }

Catching all exception types

a catch block without bracket or having type as Exception can handle all exception types.

        try{
            div = 100 / x;
            Console.WriteLine("Not executed line");
        }
        catch {
            Console.WriteLine("oException");
        }

Throwing an Exception

using throw keyword, an exception could be thrown programmatically.

public class ProgramLog {
    FileStream logFile = null!;
    public void OpenLog(FileInfo fileName, FileMode mode) { }
    public void WriteLog() {
        if (!logFile.CanWrite) {
            throw new InvalidOperationException("Logfile cannot be read-only");
        }
        // Else write data to the log and return.
    }
}

User-defined exceptions

a user-defined exception class can be created by deriving from the Exception class.

When creating your own exceptions, end the class name of the user-defined exception with the word "Exception", and implement the three common constructors, shown below code :

using System;
public class EmployeeListNotFoundException : Exception {
    public EmployeeListNotFoundException() {
    }
    public EmployeeListNotFoundException(string message)
        : base(message) {
    }
    public EmployeeListNotFoundException(string message, Exception inner)
        : base(message, inner) {
    }
}

That's it for this article, please share your feedback in the comments.

References and further reading

Did you find this article valuable?

Support Deepak Kumar Jain by becoming a sponsor. Any amount is appreciated!