How to create a custom exception in C#

By FoxLearn 12/24/2024 8:59:39 AM   16
Creating a custom exception is straightforward and can help make your application’s error-handling more precise and informative.

To create a custom exception in C#, you need to derive a class from the base Exception class.

public class CustomException : Exception
{
    // Constructor that takes a custom message
    public CustomException(string message) : base(message) { }

    // Parameterless constructor
    public CustomException() { }
}

We create a new class CustomException, which inherits from the Exception class.

The constructor allows you to pass a custom error message, which is then passed to the base class (Exception) constructor. This ensures that the error message is stored and can be accessed when the exception is thrown.

You can throw this exception just like any other built-in exception:

throw new SimpleCustomException("create a custom exception in c#");

When the exception is caught, it will display the name of the exception and the message:

CustomException: create a custom exception in c#

Why Use Custom Exceptions?

While it is possible to throw common exceptions like ArgumentException or FormatException, custom exceptions provide several advantages:

  1. Specificity: You can create highly specific exceptions for particular error conditions.
  2. Easier Logging: Custom exceptions allow for better control over logging and error monitoring. For example, a log system can easily filter out specific exception types, making it easier to track issues.
  3. Improved Error Handling: By defining custom exceptions, client code can catch and handle specific issues more appropriately. It allows the caller to make decisions based on the exact type of error.

Let’s consider an example where you want to parse a binary string, but you need to handle cases where the input string is invalid. Instead of using a generic ArgumentException, we can define a custom exception called InvalidBinaryStringException.

public class InvalidBinaryStringException : Exception
{
    public InvalidBinaryStringException(string binaryString)
        : base($"Bad binary string: {binaryString}. Binary string must be 0's and 1's and the length must be a multiple of 8. Example: 00010001.")
    {
    }
}

In this case, the custom exception takes the invalid binary string as a parameter and formats a detailed error message explaining the expected format.

We will create a class BinaryStringUtil that will parse binary strings. If the binary string is invalid (e.g., it contains characters other than 0 or 1 or if its length isn’t a multiple of 8), the method will throw our custom InvalidBinaryStringException.

using System;
using System.Text.RegularExpressions;

public class BinaryStringUtil
{
    public static byte[] Parse(string binaryString)
    {
        // Check if binary string is valid
        if (binaryString.Length % 8 != 0 || Regex.IsMatch(binaryString, "[^01]"))
        {
            throw new InvalidBinaryStringException(binaryString);
        }

        // Parse binaryString into byte[]
        // For simplicity, this part is omitted
        return new byte[] { };
    }
}

The Parse method checks:

  • If the length of the binary string is a multiple of 8 (a valid binary string must be a multiple of 8 bits).
  • If the string contains only 0s and 1s, using a regular expression.

If the string is invalid, the InvalidBinaryStringException is thrown with a detailed message indicating what went wrong and what the expected input should be.

You can now call the Parse method and handle the exception when the input is invalid:

try
{
    byte[] result = BinaryStringUtil.Parse("0101000x");
}
catch (InvalidBinaryStringException ex)
{
    Console.WriteLine(ex.Message);
}

If an invalid binary string is passed, the exception will be caught, and the message will be displayed:

Bad binary string: 0101000x. Binary string must be 0's and 1's and the length must be a multiple of 8. Example: 00010001.

Why Not Use ArgumentException or FormatException?

You may wonder why we didn’t just use ArgumentException or FormatException for this case. While both of these exceptions would work in a general sense, using a custom exception provides several benefits:

  • Clarity: It makes the error more specific. By catching InvalidBinaryStringException, client code can handle this error type separately from other generic exceptions.
  • Easy Identification: In a logging or monitoring system, it’s easier to track InvalidBinaryStringException than a generic exception type like ArgumentException with a custom message.
  • Maintainability: If the format of the binary string changes or if more validations are added in the future, having a custom exception allows you to encapsulate that logic in one place.

To ensure that your custom exceptions are thrown correctly, you can write unit tests. Using the MSTest framework, you can test that the Parse method throws the expected exception when the binary string is invalid.

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class BinaryStringUtilTests
{
    [DataRow("01")]
    [DataRow("0101000x")]
    [TestMethod]
    public void ParseTest_WhenBadBinaryString_ThrowsException(string binaryString)
    {
        Assert.ThrowsException<InvalidBinaryStringException>(() => BinaryStringUtil.Parse(binaryString));
    }
}

This test method uses the Assert.ThrowsException method to verify that the InvalidBinaryStringException is thrown when the Parse method receives invalid data. It checks cases where the string is too short or contains invalid characters.