Make the exceptions thrown by your methods more informative and useful by including more information with each exception. Do this by delegating the creation of exceptions instances to helper methods that focus on one task.
Throwing exceptions is the established C# idiom for when your methods (or instances) encounter an exceptional situation that they’re not prepared to handle.
Consider this simple method, based on one that I’ve had in my toolbox for quite some time, used to convert a string value into a specific type:
This method lets you do safe type conversions like this:
The problem with
TryConvert<T>() as written above is that the exception it throws is completely uninformative. It doesn’t really tell you anything other than something went wrong, and you don’t even know where unless you capture the stack trace.
(I’ve also had someone point out that catching
Exception is generally considered a bad idea in this method. Unfortunately,
some type converters throw
Exception directly - check out BaseNumberConverter for example.)
The first thing we can do to improve the situation is to include both an informative message and the original exception. The result looks something like this (showing only the relevant fragment to save space):
Now the exception tells us what went wrong - but, it’s still not great.
What happens if/when we’re debugging and we want to pull out the original value in order to create a new test or debug the issue in more depth? Having to select and copy just the right characters out of the message is error-prone and tedious.
We can use the
Data dictionary present on every exception to carry additional data, as follows:
The downside of this is that our original method has grown substantially in size; let’s extract the creation of the exception into a helper method:
Observe that we don’t throw the exception within the helper, we return it to be thrown by our original method, which is now substantially shorter and easier to read.
Isolating the code to create the exception within the helper class gives you a distinct space to focus on what that exception should do. As I’ve used this technique through my own code, I’ve found that this leads to code that’s easier to analyze when it fails.