Throwing Exception
When To Throw Exception
There are many circumstances when throwing exception makes sense, in this section I’ll describe them and discuss why it’s a good idea to throw them. Please note that a lot of examples in this article is simplified to prove a point. For example one does not use a method to retrieve an array element. Or in some cases I didn’t use the previously advocated techniques to focus on the current point. So naturally examples doesn’t try to be a perfect form of exception handling in every way, because that introduce extra elements that could potentially distract the reader.
1 — Completing The Process And Giving Result Is Impossible (Fail Fast)
static void FindWinner(int[] winners)
{
if (winners == null)
{
throw new System.ArgumentNullException($"Parameter {nameof(winners)} cannot be null", nameof(winners));
}
OtherMethodThatUsesTheArray(winner);
}
Suppose we have the above method, here we throw an exception because getting result from this method without the existence of winner array is impossible. One other important point is the ease of use for the user of this method. Imagine we didn’t throw an exception and the array passed into OtherMethodThatUsesTheArray
method and that method throws a NullReferenceException
. By not throwing an exception debugging becomes much harder. Because the debugger of this code must first look into the OtherMethodThatUsesTheArray
method because that’s where the error originated form. Then he figures out that the winner argument is where this error is originated. When we throw an exception we know exactly where is error is originated from and we don’t have to chase it around the code base. Another problem is unnecessary use of resource, suppose before we reach the point that an exception happens by the framework, we do some expensive processing. Now if the method cannot give us our result without the dependent parameters or what have you, we wasted a lot of resource processing when in fact the method couldn’t possibly succeed in the first place. Remember we don’t throw exception when an error might happen, but we do when the error is blocking the process. Also sometimes we could avoid using exception and use try-stuff pattern like int.TryParse
and not throwing exception. Note that exceptions are much more expensive than patterns like the one I just mentioned. Also take a look at fail-fast paradigm published by Martin Fowler (and written by Jim Shore).
2 — Calling an object’s member might produce invalid results given the object current state or might completely fail
void WriteLog(FileStream logFile)
{
if (!logFile.CanWrite)
{
throw new System.InvalidOperationException("Logfile cannot be read-only");
}
// Else write data to the log and return.
}
Here the file stream that is passed to the WriteLog method crated in such a way that is not writable. In this instance we know that this method is not going to work. Because we can’t log to a file that is not writable. Another example is when we have a class that we expect to be in specific state when we receive it. We have again fail fast by throwing an exception and save resources and debugging time.
3 — Catching A Generic Non-Specific Exception And Throwing A More Specific One
static int GetValueFromArray(int[] array, int index)
{
try
{
return array[index];
}
catch (System.IndexOutOfRangeException ex)
{
throw new ArgumentOutOfRangeException("Index is out of range", "index", ex);
}
}
There is one rule of thumb when it comes to exceptions, the more specific exception our program produce, the easier it is to debug and maintain. In other word by doing this our program produce more accurate errors. So we should always strive to throw more specific exceptions as much as possible. That’s why throwing exceptions like System.Exception
, System.SystemException
, System.NullReferenceException
, or System.IndexOutOfRangeException
is not a good idea. It’s better to not use them and see them as a signal that the error is generated by the framework and we’re going to have a tough time debugging. In the code above you see that we catch the IndexOutOfRangeException
and throw a new ArgumentOutOfRangeException
that shows us where the actual error is originated from. We can also use it to catch the framework generated exception and throw a new custom exception. That allows us to add additional information or maybe handle it differently. Just make sure you pass the original exception into the custom exception as inner exception, otherwise the stacktrace will be lost.
4 — Throw Exception For Exceptional Cases
This one might sound obvious, but it can be tricky sometimes. There is somethings in our program that are expected to happen and we can’t count them as errors. So we’ll not throw an exception. For example a search query might return empty or the user login attempt might fail. In these circumstances it’s better to return some kind of meaningful message then throwing exception. As Steve McConnell puts it in Code Complete book, “Exceptions should be reserved for what’s truly exceptional” as opposed to expected ones.
Do Not Use Exceptions To Change The Flow Of The Program
Take the below code for example.
[HttpPost]
public ViewResult CreateProduct(CreateProductViewModel viewModel)
{
try
{
ValidateProductViewModel(viewModel);
CreateProduct(viewModel);
}
catch (ValidationException ex)
{
return View(viewModel);
}
}
This is a pattern that I see in some legacy code that I need to work on. As you can see the method ValidateProductViewModel
throws an exception for some reason if the view model was not valid. Then if the view model was not valid it catches it and return the view with errors. We better change the code above to the code below?!
[HttpPost]
public ViewResult CreateProduct(CreateProduct viewModel)
{
bool viewModelIsValid = ValidateProductViewModel(viewModel);
if(!viewModelIsValid) return View(viewModel);
CreateProduct(viewModel);
return View(viewModel);
}
Here, the method that is responsible for validation returns a bool instead of throwing exception if the view model was not valid. Here’s a good question on Stackoverflow about this.
Do Not Return Error Code, Throw Exception Instead
Throwing exception is always safer than return an error code. The reason is what if the calling code forgot to check for or returned error code and went ahead with execution? But that can’t happen if we throw an exception.
Make Sure To Clean Up Any Side Effect If Exception Thrown
private static void MakeDeposit(Account account,decimal amount)
{
try
{
account.Deposit(amount);
}
catch
{
account.RollbackDeposit(amount);
throw;
}
}
Here we know that an error might happen when calling the deposit method. We should make sure if an exception happens, any changes to the system is rolled back.
try
{
DBConnection.Save();
}
catch
{
// Roll back the DB changes so they aren't corrupted on ANY exception
DBConnection.Rollback();
// Re-throw the exception, it's critical that the user knows that it failed to save
throw;
}
You can also use transaction scope instead of handling it this way. Remember you can do it in finally block too.
If You Catch An Exception And Cannot Handle It Properly, Make Sure To Re-Throw It
There are circumstances when we catch an exception but we don’t intend to handle it, maybe we just want to log it. Something Like:
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex.Message, ex);
//bad practice, stack trace is lost
//throw ex;
//good practice, keepi'n the stack trace
throw;
}
As you can see in the code above, not only we should re-throw the exception, but also we should re-throw it in a way that we don’t loose the stack trace. In this case if we use throw ex
, we’ll lose the stacktrace, but if we use throw without an instace ex in front of it, we preserved the stackstrace.
Do Not Use Exceptions As Parameter Or Return Value
Using Exception as argument or return value doesn’t make sense most of the time. Maybe the only time that it might make sense is when we use it in an exception factory, something like.
Exception AnalyzeHttpError(int errorCode) {
if (errorCode < 400) {
throw new NotAnErrorException();
}
switch (errorCode) {
case 403:
return new ForbiddenException();
case 404:
return new NotFoundException();
case 500:
return new InternalServerErrorException();
…
default:
throw new UnknownHttpErrorCodeException(errorCode);
}
}
Prevent The Program From Throwing Exception If Possible, Exceptions Are Expensive
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
Take the above code for example, we put the code for closing the connection in try block. If the connection is already closed, it’ll throws an exception. But maybe we could achieve the same thing without causing the program to throw exception?
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
As you can see the try block was unnecessary, it caused the program to throw an exception if the connection was already closed, and exception are more expensive than if check.
Create Your Own Exception Class
Not all exceptions that can happen are covered by the framework. Sometimes we need to create our own exception type. They can be defined as class, like any other classes in C#. We create custom exception classes usually because we want to handle that exception differently. Or that specific kind of exception is very critical to our application. To create a custom exception we need to create a class that is derived from System.Exception.
[Serializable()]
public class InvalidDepartmentException : System.Exception
{
public InvalidDepartmentException() : base() { }
public InvalidDepartmentException(string message) : base(message) { }
public InvalidDepartmentException(string message, System.Exception inner) : base(message, inner) { }
// A constructor is needed for serialization when an exception propagates from a remoting server to the client.
protected InvalidDepartmentException(SerializationInfo info, StreamingContext context) { }
}
Here the The derived class defined four constructors. one default constructor, one that sets the message property, and one that sets both the Message and InnerException
. The fourth constructor is used to serialize the exception. Also note that exceptions should be serializable.