Custom Exceptions in C#: When and How to Use Them
In C#, the standard library provides a rich set of exceptions like ArgumentNullException, InvalidOperationException, and FileNotFoundException. However, sometimes your application encounters errors that are specific to your business logic—errors that standard exceptions simply cannot describe accurately.
This is where Custom Exceptions come in. They allow you to create specific, meaningful error types that make your code easier to debug and maintain.
1. How to Create a Custom Exception
To create a custom exception, you simply create a class that inherits from the base Exception class.
Best Practice: Always implement the three standard constructors so your exception behaves like any other .NET exception.
2. When to Use Custom Exceptions
You should not create a custom exception for every possible error. Overusing them adds unnecessary complexity. Use them only when:
- You need to handle this specific error differently: If you want to catch only this error and let others bubble up, a custom type is essential.
- You need to carry additional data: Standard exceptions only allow a string message. Custom exceptions can carry properties (like
ErrorCode,UserId, orRetryAfter). - No standard exception fits: If
InvalidOperationExceptionfeels too vague, create a specific one likeOrderAlreadyShippedException.
Decision Logic:
- Is the argument null? -> Use
ArgumentNullException. - Is the file missing? -> Use
FileNotFoundException. - Did the user try to withdraw more money than they have? -> Create
InsufficientFundsException.
3. How to Throw and Catch
Here is how you use the custom exception we defined above. Notice how much cleaner the logic becomes.
Throwing the Exception
Catching the Exception
When catching, we can now target this specific error type. This allows us to show a helpful message to the user, while letting "real" crashes (like NullReferenceException) bubble up to be logged.
Summary
| Feature | Standard Exception | Custom Exception |
| Purpose | General errors (Nulls, IO, Math) | Domain-specific errors (Business Logic) |
| Data | Message (String) | Any Properties (Int, Objects, Enums) |
| Handling | Generic catch blocks | Specific catch blocks |
| Example | ArgumentException | InvalidPaymentException |