Custom Exceptions in C#

Learn how to implement custom exceptions in C# and why they're useful.

An exception is a runtime error in a program that violates a system or application constraint, or a condition that is not expected to occur during normal execution of the program. Possible causes of exceptions include attempting to connect to a database that no longer exists, when a program tries to divide a number by zero, or opening a corrupted XML file. When these occur, the system catches the error and raises an exception.

Exception Classes in .NET

In .NET, an exception is represented by an object instance with properties to indicate where in the code the exception was encountered and a brief description of what caused the exception. We have different exception classes representing different types of errors and they all inherit from the System.Exception base class. The SystemException class inherits from the Exception base class. The OutOfMemoryException, StackOverflowException, and ArgumentException classes inherit from SystemException. The ArgumentException class has two other classes which derive from it: the ArgumentNullException and ArgumentOutOfRangeException classes. The ArithmeticException class derives from the Exception base class. The OverflowException and DivideByZero exceptions then inherit from the ArithmeticException class. We also have the ApplicationException class, which is derived from Exception base class.

Additionally, we can define our own exception classes and they can derive from the Exception base class. The exceptions we define in our projects are called user-defined or custom exceptions. A use case for creating our own exception class is when you’re interfacing with some external service that returns error codes to indicate errors. You can then have a way to translate the error codes into custom exceptions using something like the Gateway or Facade design patterns.

Defining Custom Exception

When creating custom exception classes, they should inherit from the System.Exception class (or any of your other custom exception classes from the previous section). The class name should end with the word Exception, and it should implement at least the three common constructors of exception types.

Let’s look at an example application that should raise an exception when an account balance is less than the transaction amount. Create a new console application project. Add a file InsufficientFuncException.cs with the following class definition:

[System.Serializable]
public class InsufficientFuncException : System.Exception
{
    private static readonly string DefaultMessage = "Account balance is insufficient for the transaction.";

    public string AccountName { get; set; }
    public int AccountBalance { get; set; }
    public int TransactionAmount { get; set; }

    public InsufficientFuncException() : base(DefaultMessage) { }
    public InsufficientFuncException(string message) : base(message) { }
    public InsufficientFuncException(string message, System.Exception innerException)
    : base(message, innerException) { }

    public InsufficientFuncException(string accountName, int accountBalance, int transactionAmount)
    : base(DefaultMessage)
    {
        AccountName = accountName;
        AccountBalance = accountBalance;
        TransactionAmount = transactionAmount;
    }

    public InsufficientFuncException(string accountName, int accountBalance, int transactionAmount, System.Exception innerException)
    : base(DefaultMessage, innerException)
    {
        AccountName = accountName;
        AccountBalance = accountBalance;
        TransactionAmount = transactionAmount;
    }

    protected InsufficientFuncException(
        System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context) : base(info, context) { }

}

We defined an exception class named InsufficientFuncException which derives from the System.Exception base class. It contains the properties TransactionAmount, AccountBalance and AccountName, which will help provide more info about the error. We also have a default message variable which will be set as the Message property when no message argument is supplied from the constructor. The first three public constructors are the three standard constructors for exception types. The other constructors accept arguments accountName to indicate the owner of the account, accountBalance to indicate the current account balance, and transactionAmount so we know how much was requested for the transaction. We also marked the class as serializable so it can be used across app domains.

Using Custom Exception

Custom exceptions are thrown and caught the same way as built-in exception types in .NET. To use the custom exception we defined, add a new file Account.cs with the following content:

class Account
{
    public Account(string name, int balance)
    {
        Name = name;
        Balance = balance;
    }
    public string Name { get; private set; }
    public int Balance { get; private set; }

    public void Debit(int amount)
    {
        if (Balance < amount) throw new InsufficientFuncException(Name, Balance, amount);
        Balance = Balance - amount;
    }

    public void Credit(int amount) => Balance = amount + Balance;
}

This class holds the account details with methods to add and subtract from the balance. The InsufficientFuncException exception is thrown when the Debit() method is called with a transaction amount lower than the account balance.

We will now use this class and perform a debit transaction and see this exception class being utilized. Update Program.cs with the code below.

using System;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World Bank!");
            var account = new Account("James Beach", 150);
            try
            {
                account.Debit(200);
            }
            catch (InsufficientFuncException ex)
            {
                Console.WriteLine("Encountered exception \nException Message: " + ex.Message);
                Console.WriteLine("Account Balance: " + ex.AccountBalance);
                Console.WriteLine("Transaction Amount: " + ex.TransactionAmount);
            }

            Console.Read();
        }
    }
}

The code above creates an Account object with a balance of 150. Then it calls the Debit() method with an amount of 200, which is higher than the balance. This should throw an exception, and we’ll log that information to the console. When you run the program, you should get the following in the console.

Hello World Bank!
Encountered exception
Exception Message: Account balance is insufficient for the transaction.
Account Balance: 150
Transaction Amount: 200

You should notice that it catches the exception, and the properties of the exception type we defined makes it easy to tell which account had this error, the account balance, and the requested transaction amount.

That’s A Wrap!

Custom exceptions are exception types you define in your project. They’re useful when the built-in exception types don’t meet your needs. For example, if you’re building a library or framework, and you want consumers of that library to react to exceptions from the library differently than they would for built-in exception types. These user-defined exception classes should inherit from the System.Exception base class and implement the three common constructors found in exception types.


Peter Mbanugo
About the Author

Peter Mbanugo

Peter Mbanugo is a software developer, tech writer, and maker of Hamoni Sync. He currently works with Field Intelligence, where he helps build logistic and supply chain apps. He's also a contributor to Hoodie and a member of the Offline-First community. You can follow him on Twitter.

Related Posts

Comments

Comments are disabled in preview mode.