.NET has a large number of built in exceptions. However, there maybe times when none of the built exceptions seem adequate for your particular scenario and you will need to create your own custom (AKA “user defined”) exception.
This post focuses on a discussion of how to create custom exceptions in .NET. The different types of built in .NET exceptions, exception handling, throwing, and more general exception related best practices will not be covered.
All code examples are in C#.
Basic Custom Exception Example
The following example illustrates the most basic template that every custom exception should follow:
Example notes:
- The class inherits from
Exception
. Custom exceptions should not inherit fromApplicationException
. In the early days of .NET Microsoft actually recommended inheriting fromApplicationException
but has since changed that recommendation. - The class is decorated with the
Serializable
attribute. Custom exceptions are not serializable by default. But making a custom exception serializable allows it to be properly marshalled across app domains and threads. It should be noted that simply decorating your custom exception withSerializable
is not enough to serialize any extra properties you might have defined. More on this in the “Extended Custom Exception Example” section below. - The class should always (unless there is a very good reason) have the four Microsoft recommended constructors:
public ()
,public (string message)
,public (string message, Exception innerException)
,protected (SerializationInfo info, StreamingContext context)
. - The custom exception class itself is not marked as implementing
ISerializable
. Doing so would be redundant as we are inheriting fromException
which itself implementsISerializable
. - We provide a default message that makes sense for the custom exception when the parameterless constructor is called.
- The custom exception’s name ends with “Exception”. All exceptions in .NET should have the name suffix of “Exception”.
- Any XML documentation comments have not been added for the sake of brevity. However, adding them to the public interface of the exception can be a good idea to aid in it’s use.
Extended Custom Exception Example
In the following example we build upon our basic example from the previous section and add the concept of adding our own property. In this case an integer property EntityId
:
Example notes:
- Additional new properties, in this case
EntityId
, should not have non-private setters. This is because .NET exceptions should be immutable (once created their state should not change). Instead all values should be passed through and set in the constructor. - A new method has been added:
GetObjectData
. This method is part of theISerializable
interface whichException
implements. When overridden it allows us to set extra information that we want to save during serialization. In this case the integer value in our propertyEntityId
. When overriding this method make sure to also call the base implementation. - Our protected constructor which was previously empty now has implementation to help set our new
EntityId
property when our exception is being deserialized.
Unit Testing Custom Exceptions
We can unit test our extended custom exception example with the following tests:
The tests cover five scenarios: the four different constructors and what properties they set. The fifth test checks the serialization/deserialization of the exception and it’s EntityId
property value.
Final Thoughts
So when should we create our own custom exception instead of using a built in .NET exception? If a .NET built in exception fits your requirement then you should certainly use it. However, if you do go down the route of creating your own custom exception here are some things to bear in mind:
- Don’t create custom exceptions for every type of situation. Be wary if a custom exception is too granular. Instead generalize to one custom exception and be more specific in the exception’s message when the exception is thrown.
- Don’t create complex hierarchies of custom exceptions for your application. Most of the time it will make the most sense that your custom exception inherits directly from
Exception
. - Do create a custom exception to signify a problem in your particular application domain. For example creating a custom exception that represents that a problem has occurred in a NuGet package library you have created.
- When creating custom exceptions do think about the exception handling that a consumer of your code might have to deal with. For example if you create a number of different custom exceptions is the consumer of your code going to have a number of different
catch
blocks? It might also be worth considering how you are going to communicate that your exception exists and under what scenarios it will be thrown to the consumer of your code.
As of writing .NET is nearly 20 years old and there are many posts on the internet about creating custom exceptions. However, virtually all only cover small parts of the topic or unfortunately give bad advice. Hopefully this post has provided a more complete picture on how to create custom exceptions.