C# – How to create a custom exception

To create a custom exception, create a subclass of the Exception class, like this:

public class SimpleCustomException : Exception
{
	public SimpleCustomException(string message) : base(message) { }
	public SimpleCustomException() { }
}
Code language: C# (cs)

Then throw it just like you would any other exception, like this:

throw new SimpleCustomException("Cannot call this method on days that end with 'y'")
Code language: C# (cs)

It’s a good idea to call the base constructor from your constructor and pass in your custom error message. If this exception is unhandled, or if you are logging the exception, then it’ll show the exception name and message, like this:

SimpleCustomException: Cannot call this method on days that end with 'y'Code language: plaintext (plaintext)

Example – Throwing a custom exception when invalid data is passed in

Creating your own custom exceptions allows you to create very specific exceptions. Throwing and catching very specific exceptions is part of clean error handling.

The following example shows a binary string parser class that throws a custom exception when invalid data is passed in. It throws a very specific exception explaining exactly what the problem is and what data is expected.

1 – Add a custom exception class

This custom exception is taking the invalid binaryString and putting it into an error message that explains what the expected format is, and includes an example of valid input.

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: 00000001.")
	{
	}
}
Code language: C# (cs)

2 – Throw the exception when error conditions are detected

Before trying to parse the binary string, the BinaryStringUtil class checks the passed in binaryString, and throws the custom InvalidBinaryStringException if the input is invalid.

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

		//Parse binaryString into byte[]
		
		return new byte[] { };

	}
}
Code language: C# (cs)

You may be wondering, why not throw ArgumentException or FormatException instead? True, you could throw these exceptions and pass in the same error message. However, consider the following reasons for using custom exceptions:

  • You encapsulate error messages. Notice how the code above is simply passing in the binaryString to the exception?
  • Let’s say you’re using a log monitoring tool (like Splunk) and want to send alert emails when this specific error happens. You can simply look for “InvalidBinaryStringException” in the log. If you were using ArgumentException, you’d have to look for the error message instead (“Bad binary string…”).
  • The client code can catch InvalidBinaryStringException and handle it properly. Let’s say your parsing code has a bug and some method you’re calling is throwing ArgumentException. If the client were catching this non-specific exception, the client code would have the wrong error handling behavior, and your bug would be hidden.

3 – Add unit tests to prove the code throws exceptions

The following parameterized unit tests tests the two error conditions: when the binary string isn’t a valid length and when it has invalid characters.

[DataRow("01")]
[DataRow("0100000a")]
[TestMethod()]
public void ParseTest_WhenBadBinaryString_ThrowsException(string binaryString)
{
   Assert.ThrowsException<InvalidBinaryStringException>(() => BinaryStringUtil.Parse(binaryString));
}

Code language: C# (cs)

Notice that it’s using Assert.ThrowsException instead of the ExpectedException attribute.

Comments are closed.