C# Custom Exceptions

Summary: in this tutorial, you’ll learn about C# custom exceptions and how to create and use a custom exception in your program.

Introduction to the C# custom exceptions

C# provides you with many built-in exception classes. Sometimes, these built-in exception classes are not sufficient to describe the error in your application. In this case, you can define custom exception classes.

By definition, a custom exception is an exception that you create to represent a specific error in your application.

To create a custom exception, you define a class that inherits from the System.Exception class. By convention, an exception class ends with Exception like MyCustomException:

class MyCustomException: Exception
{
}Code language: C# (cs)

A custom exception class should have three standard constructors:

class MyCustomException: Exception
{
    MyCustomException(): base() { }
    MyCustomException(string message) : base(message) { }
    MyCustomException(string message, Exception innerException) : base(message, innerException) { }
}Code language: C# (cs)

In this example:

  • The first constructor is a default constructor that takes no arguments.
  • The second constructor takes a string message that describes the exception.
  • The third constructor takes both a message and an inner exception, which you can use to chain exceptions together to describe the error in more detail.

A custom exception class may also have additional properties to provide more information about the error that occurs.

C# custom exception example

Let’s take a look at an example of using a custom exception.

using static System.Console;

public class Account
{
    private decimal balance;
    public decimal Balance => balance;

    public Account(decimal initialBalance = 0)
    {
        if (initialBalance < 0)
        {
            throw new ArgumentOutOfRangeException($"The {nameof(initialBalance)} must be zero or positive.");
        }
        balance = initialBalance;
    }

    public Account Withdraw(decimal amount)
    {
        if (amount > Balance)
        {
            throw new ArgumentOutOfRangeException(
                nameof(amount),
                $"Could not withdraw an amount ({amount:C}) that is more than balance ({Balance:C})"
            );
        }

        balance -= amount;
        return this;
    }
    public Account Deposit(decimal amount)
    {
        if (amount <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(amount));
        }
        balance += amount;
        return this;
    }
}

public class Program
{
    public static void Main(string[] args)
    {

        var account = new Account(100);
        try
        {
            account.Withdraw(200);
        }
        catch (ArgumentOutOfRangeException ex)
        {
            WriteLine(ex.Message);
        }
        catch (Exception ex)
        {
            WriteLine(ex.Message);
        }

        ReadLine();
    }
}Code language: C# (cs)

Output:

Could not withdraw an amount ($200.00) that exceeds the balance ($100.00) (Parameter 'amount')Code language: C# (cs)

How it works.

First, define an Account class that represents a bank account with a balance. The Account class has two methods for depositing and withdrawing money. These methods use a built-in exception class ArgumentOutOfRangeException to prevent invalid operations.

Second, create a new Account object in the Main() method of the Program class with an initial balance of 100. Then, attempt to withdraw 200 from the account using the Withdraw() method.

Because the balance is insufficient, the Withdraw() method throws an ArgumentOutOfRangeException.

The catch block handles the ArgumentOutOfRangeException exception by displaying the error message on the console.

The program works fine except that the ArgumentOutOfRangeException is not very expressive and the error message doesn’t show the deficit.

To improve this, we can create a new custom exception as follows:

using static System.Console;

public class InsufficientFundException : Exception
{
    public decimal Deficit
    {
        get;
        set;
    }
    public InsufficientFundException()
        : base()
    {
    }

    public InsufficientFundException(
        string message
    )
        : base(message)
    {
    }

    public InsufficientFundException(
      string message,
      Exception innerException
  ) : base(message, innerException)
    {

    }

    public InsufficientFundException(decimal deficit)
    {
        Deficit = deficit;
    }


    public override string Message =>
         $"{base.Message} Could not withdraw due to a deficit of {Deficit:C}";


}
public class Account
{

    private decimal balance;
    public decimal Balance => balance;

    public Account(decimal initialBalance = 0)
    {
        if (initialBalance < 0)
        {
            throw new ArgumentOutOfRangeException($"The {nameof(initialBalance)} must be zero or positive.");
        }
        balance = initialBalance;
    }

    public Account Withdraw(decimal amount)
    {
        if (amount > Balance)
        {
            var deficit = amount - Balance;
            throw new InsufficientFundException(deficit);

        }

        balance -= amount;
        return this;
    }
    public Account Deposit(decimal amount)
    {
        if (amount <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(amount));
        }
        balance += amount;
        return this;
    }
}

public class Program
{
    public static void Main(string[] args)
    {

        var account = new Account(100);
        try
        {
            account.Withdraw(200);
        }
        catch (InsufficientFundException ex)
        {
            WriteLine(ex.Message);
        }
        catch (Exception ex)
        {
            WriteLine(ex.Message);
        }

        ReadLine();
    }
}Code language: C# (cs)

Output:

Exception of type 'InsufficientFundException' was thrown. Could not withdraw due to a deficit of $100.00Code language: C# (cs)

How it works.

First, define a new custom exception class named InsufficientFundException that inherits from the Exception class.

public class InsufficientFundException : Exception
{
    public decimal Deficit
    {
        get;
        set;
    }
    public InsufficientFundException()
        : base()
    {
    }

    public InsufficientFundException(
        string message
    )
        : base(message)
    {
    }

    public InsufficientFundException(
      string message,
      Exception innerException
  ) : base(message, innerException)
    {

    }

    public InsufficientFundException(decimal deficit)
    {
        Deficit = deficit;
    }


    public override string Message =>
         $"{base.Message} Could not withdraw due to a deficit of {Deficit:C}";

}Code language: C# (cs)

The InsufficientFundException class implements three standard constructors.

The InsufficientFundException class also has a fourth constructor that takes a decimal parameter called deficit, which represents the deficit when attempting to withdraw an amount that exceeds the balance. This constructor sets the Deficit property to the deficit parameter.

The InsufficientFundException class extends the Message property of the Exception class. It concatenates the Message of the base class with a custom message that includes the deficit.

In the Withdraw() method of the Account class, instead of throwing the ArgumentOutOfRangeException, it throws the InsufficientFundException exception and pass the deficit to its constructor.

public Account Withdraw(decimal amount)
{
    if (amount > Balance)
    {
        var deficit = amount - Balance;
        throw new InsufficientFundException(deficit);
    }

    balance -= amount;
    return this;
}Code language: C# (cs)

In the Main method of the Program class, instead of catching the ArgumentOutOfRangeException exception, we catch the ArgumentOutOfRangeException and display the error message on the console:

public class Program
{
    public static void Main(string[] args)
    {
        var account = new Account(100);
        try
        {
            account.Withdraw(200);
        }
        catch (ArgumentOutOfRangeException ex)
        {
            WriteLine(ex.Message);
        }
        catch (Exception ex)
        {
            WriteLine(ex.Message);
        }
    }
}Code language: C# (cs)

Summary

  • Only create a custom class exception class if it provides more meaningful and detailed information about the error in your application.
  • Extend the built-in System.Exception class to define a custom exception class.
  • Throw a custom exception by using the throw keyword followed by the custom exception object.
  • Use the try...catch block to catch a custom exception by specifying the custom exception type in the catch block.
Was this tutorial helpful ?