An exception is a blocking error.
First of all, the best practice should be don't throw exceptions for any kind of error, unless it's a blocking error.
If the error is blocking, then throw the exception. Once the exception is already thrown, there's no need to hide it because it's exceptional; let the user know about it (you should reformat the whole exception to something useful to the user in the UI).
Your job as software developer is to endeavour to prevent an exceptional case where some parameter or runtime situation may end in an exception. That is, exceptions mustn't be muted, but these must be avoided.
For example, if you know that some integer input could come with an invalid format, use int.TryParse
instead of int.Parse
. There is a lot of cases where you can do this instead of just saying "if it fails, simply throw an exception".
Throwing exceptions is expensive.
If, after all, an exception is thrown, instead of writing the exception to the log once it has been thrown, one of best practices is catching it in a first-chance exception handler. For example:
- ASP.NET: Global.asax Application_Error
- Others: AppDomain.FirstChanceException event.
My stance is that local try/catches are better suited for handling special cases where you may translate an exception into another, or when you want to "mute" it for a very, very, very, very, very special case (a library bug throwing an unrelated exception that you need to mute in order to workaround the whole bug).
For the rest of the cases:
- Try to avoid exceptions.
- If this isn't possible: first-chance exception handlers.
- Or use a PostSharp aspect (AOP).
Answering to @thewhiteambit on some comment...
@thewhiteambit said:
Exceptions are not Fatal-Errors, they are Exceptions! Sometimes they
are not even Errors, but to consider them Fatal-Errors is completely
false understanding of what Exceptions are.
First of all, how an exception can't be even an error?
- No database connection => exception.
- Invalid string format to parse to some type => exception
- Trying to parse JSON and while input isn't actually JSON => exception
- Argument
null
while object was expected => exception
- Some library has a bug => throws an unexpected exception
- There's a socket connection and it gets disconnected. Then you try to send a message => exception
- ...
We might list 1k cases of when an exception is thrown, and after all, any of the possible cases will be an error.
An exception is an error, because at the end of the day it is an object which collects diagnostic information -- it has a message and it happens when something goes wrong.
No one would throw an exception when there's no exceptional case. Exceptions should be blocking errors because once they're thrown, if you don't try to fall into the use try/catch and exceptions to implement control flow they mean your application/service will stop the operation that entered into an exceptional case.
Also, I suggest everyone to check the fail-fast paradigm published by Martin Fowler (and written by Jim Shore). This is how I always understood how to handle exceptions, even before I got to this document some time ago.
[...] consider them Fatal-Errors is completely false understanding of what exceptions are.
Usually exceptions cut some operation flow and they're handled to convert them to human-understandable errors. Thus, it seems like an exception actually is a better paradigm to handle error cases and work on them to avoid an application/service complete crash and notify the user/consumer that something went wrong.
More answers about @thewhiteambit concerns
For example in case of a missing Database-Connection the program could
exceptionally continue with writing to a local file and send the
changes to the Database once it is available again. Your invalid
String-To-Number casting could be tried to parse again with
language-local interpretation on Exception, like as you try default
English language to Parse("1,5") fails and you try it with German
interpretation again which is completely fine because we use comma
instead of point as separator. You see these Exceptions must not even
be blocking, they only need some Exception-handling.
If your app might work offline without persisting data to database, you shouldn't use exceptions, as implementing control flow using try/catch
is considered as an anti-pattern. Offline work is a possible use case, so you implement control flow to check if database is accessible or not, you don't wait until it's unreachable.
The parsing thing is also an expected case (not EXCEPTIONAL CASE). If you expect this, you don't use exceptions to do control flow!. You get some metadata from the user to know what his/her culture is and you use formatters for this! .NET supports this and other environments too, and an exception because number formatting must be avoided if you expect a culture-specific usage of your application/service.
An unhandled Exception usually becomes an Error, but Exceptions itself
are not codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors
This article is just an opinion or a point of view of the author.
Since Wikipedia can be also just the opinion of articule author(s), I wouldn't say it's the dogma, but check what Coding by exception article says somewhere in some paragraph:
[...] Using these exceptions to handle specific errors that arise to
continue the program is called coding by exception. This anti-pattern can quickly degrade software in performance and maintainability.
It also says somewhere:
Incorrect exception usage
Often coding by exception can lead to further issues in the software
with incorrect exception usage. In addition to using exception
handling for a unique problem, incorrect exception usage takes this
further by executing code even after the exception is raised. This
poor programming method resembles the goto method in many software
languages but only occurs after a problem in the software is detected.
Honestly, I believe that software can't be developed don't taking use cases seriously. If you know that...
- Your database can go offline...
- Some file can be locked...
- Some formatting might be not supported...
- Some domain validation might fail...
- Your app should work in offline mode...
- whatever use case...
...you won't use exceptions for that. You would support these use cases using regular control flow.
And if some unexpected use case isn't covered, your code will fail fast, because it'll throw an exception. Right, because an exception is an exceptional case.
In the other hand, and finally, sometimes you cover exceptional cases throwing expected exceptions, but you don't throw them to implement control flow. You do it because you want to notify upper layers that you don't support some use case or your code fails to work with some given arguments or environment data/properties.
NullReference
orArgumentNull
that is not part of application flow) it means that there's a bug that needs to be fixed so logging them will help to debug your code much faster. – Leri