Can anyone explain what exactly Microsoft means on the below linked page when it says:
"Do not catch an exception unless you can handle it and leave the application in a known state."
Can anyone explain what exactly Microsoft means on the below linked page when it says:
"Do not catch an exception unless you can handle it and leave the application in a known state."
Else...
throw it along... someone up the call stack will have to handle it.
If no-one up the calling stack handles it, it'll just bubble up until it reaches the UI an at least the user will know there's something wrong.
But, as everything in this programming world, there are exceptions (no pun intended):
Sometimes in a finally
block you try to close a database resource that could or could have not been initialized. In that case there's a probability of calling close() on a null object. In that case silently catching NullPointerException
is OK. You cannot handle it anyway. Although, as as @marco-borchert, checking for null would be cleaner.
Another possible exception to the rule is, as @marco-borchert also pointed out, to log the error and re-throw. Maybe you have info in that scope that won't be available up the call stack and it would be interesting to log even if you will not handle the exception at that level.
It means not littering your code with exception handlers. It also means don't do this:
try
{
//Your code here
}
catch (Exception exception)
{
}
Why is this bad? Catching any exception is not desirable because what if the system is giving you a system exception or out of memory exception? Do you really want your application to continue when the system is telling you there is no more memory?!
So, exception handlers should be specific and should be recoverable. You can always have a high level generic application level exception handler to log those cases when something unexpected occurred rather crashing your application. This is done so your application can shut down gracefully. But other than that, keep exception handlers to a minimum and specific to certain scenarios.
"Astronauts have a saying that, in space, there is no problem so bad you can't make it worse." - Chris Hatfeld
When we catch exceptions, we have taken a very unusual path through the code. Typically function calls enter at the top of the function body, and end at either the end of the body or at a special return
statement. When an exception occurs, neither of those two things happen. We instead follow a completely different path through the code using stack unwinding. Invariants may fall apart in this case.
When we do this, it is very possible to have unintended side effects because we skipped past several layers. Consider this faulty bank logic function:
bool transfer(Account accountFrom, Account accountTo, int amount)
{
try {
accountFrom.debit(amount); // remove the specified amount from the account
} catch (NotEnoughFunds ex) {
return false; // we failed
}
accountTo.credit(amount);
return true;
}
Now this is pathologically bad as far as banking apps can go, but we do see some attempt to catch the exceptions. However, let's say accountTo.credit(amount)
raises a NotImplementedException
. What happens now? We unwind the stack, and maybe the layer above prints a helpful log message before continuing. This would be where the real problem occurs. The exception that occurred has invalidated an assumption of the banking application: we have debited one account but not credited another. If we "handle" it, then this assumption is no longer valid from here on out, and the programmer likely never accounted for this. The accountFrom
may get audited to explain where this money went, or the entire system may come crashing down as the system tries to balance an equation that is now unbalanceable.
If you want to catch an exception, you need to understand that exception well enough to return the system to a state where no invariants have been broken. In the best of all cases, the code you called does this for you, because the author wrote the code with exceptions in mind. However, it is very common for an author to be exception unaware yet use a library under the hood which is exception aware. In this case, you may catch the underlying library's exception, and maintain its invariants, but fail to correct the invariants of the layer inbetween.
If an invariant is violated, there are very few things we can do. In many environments, it is valid to simply exit the application with an error saying "someone did something I didn't think was possible... we're stopping now." In other environments you are expected to cauterize the wound, isolating how much damage the invariant can do, and excising the damage in a way which doesn't break any more invariants. For example, you might design a system around C#'s AppDomain system, where individual components are separated into different AppDomains and are only allowed to communicate via channels you control. You may be able to design the rules of those channels such that when a thread in one domain throws an unhandled exception, you can tear down that entire AppDomain (because its "invariants" are no longer trusted), and then use your control over the communication channels to manage the side effects of that domain being removed in a way which preserves the invariants of the other domains.
Although as the quote says the correct way to handle exceptions is to let them pass up--when you are at the top level, for instance if you are creating your own thread and it has a top-level loop, you nearly always want to catch all exceptions so that your thread doesn't break out and die.
You either want to catch all the exceptions inside the loop so it can continue (at least log them!) or you want to catch them outside the loop, log them and halt the thread. This is what allows you to mostly ignore exceptions in other parts of your program--knowing that it will be handled correctly somewhere up the call chain.
This is true for thread loops, for your main system loop (if you have one) and for any event handlers that don't already have predefined exception handling that suits your needs.
When there is an exception that you don't know how to handle, either there is someone else who knows how to handle it or not. And either the exception happens or it doesn't.
If it doesn't happen, catching the exception makes no difference but is a waste of effort. If it happens and nobody knows how to handle it and you catch it, there's no benefit coming from that - the only benefit would come from someone figuring out how to handle it. So again, a waste of effort. And if there is someone who knows how to handle it, catching it doesn't help but might keep someone from handling it properly.
There is no situation where catching an exception that you don't know how to handle would be helpful.