Throw VS Throw e VS Throw new Exception()
Throwing exceptions seems pretty simple to anyone experienced with C#. And yet I've seen misuse of them too many times. If used incorrectly you could send someone on a wild goose chase while trying to investigate an issue or confuse your end user til they end up opening s ticket or contacting support for what they could easily handle themselves.
There are three main methods of rethrowing an exception. Each has it's use, but the TL;DR is you should probably just be using throw
and this is why:
throw
You should almost always just use throw
. This passes the exception on so it can "bubble up" like it was never caught at all. This can be very useful for instances where you need to clean up anything that's an immediate concern but still allow the calling code to handle the error.
More importantly, throw
does nothing else (except change the line number). As you'll see with the other usages below, throw
is the only variant that throws the original exception. This means the stack trace is intact and usable by anyone investigating any issues.
Say you're making a library that provides methods for encrypting and decrypting files. If the file that is passed in doesn't exist, it eventually throws a null reference deep in your library. The stack trace bubbles up and you catch it, clean up any resources and rethrow the exception like this:
try
{
// ...Some internal calls to encrypt/decrypt
}
catch (Exception e)
{
// ...Clean up
throw;
}
What do we get for a stack trace? A complete trail from calling code to the site of the null reference. Useful for open source projects or closed source projects where internal details leaking out aren't an issue.
throw e
There is one legitimate use for this: hiding details to sensitive implementations in a closed source app. It's not a great use and is more likely to cause you and your users pain in the future.
There is never a reason to use throw e
in an open source application.
See Warning CA2200.
throw new CustomException(e)
This is the better alternative to throw e
. This is technically the same syntax you use to initially throw an exception anyway, and is perfect for situations where you need to hide implementation details of point the user towards a solution.
You've probably seen a stack trace before that had the term "innerException" in it. This is how you do that.
try
{
// ...Some internal calls to encrypt/decrypt
}
catch (Exception e)
{
// ...Clean up
throw new EncryptionException(e);
}
By creating a custom exception or using an existing exception that can use the original exception as an inner exception, we can provide more accurate details on what occurred and still preserve the original stacktrace. Or if we need to hide the implementation details, we can give an exception that describes the problem in enough detail that we aren't scratching heads later wondering what on the entire call chain was null.
Generally this is a better practice. If you are catching the exception, do something. Even if all you do is add some details that make it more obvious what went wrong.
Caveats
throw
is not good for large try/catch blocks
I generally recommended using just throw
and while that's generally true, it does have one side effect that can cause some pain. The line number is changed to the throw
statement instead of the line that actually threw the exception. This is generally fine, but anyone wrapping a larger section of code (for whatever reason) should always wrap the exception with a custom exception type. It's more work to write and maintain, but the first time you have to hunt for an exception in 500+ lines of code, you'll thank yourself.
Don't use exceptions to control application flow
If you are handling exceptions and throwing them to bypass logic, you are playing a dangerous game. Consider handling exceptions in all but the unexpected scenarios and returning a value that indicates whether or not there was a problem. In my experience people pay more attention to return types than possible exceptions. If your return includes a case that handles the failure, they are more likely to plan for it and less likely to accidentally bypass important business logic because of an exception.
Subscribe to my newsletter
Read articles from Curtis Carter directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Curtis Carter
Curtis Carter
I'm a cross-platform (Windows/Linux) dev with deep experience in C# and the .Net stack (from legacy 1.1 through core and .Net 5). I like creating developer tools and teaching others about code and architecture.