This question has been asked multiple times before: why is “try { … } catch {}” a bad practice? ([0], [1], [2]). All of those answers are good, but I would like to address this issue from slightly different points.
By no means I am trying to say to avoid throwing exceptions when invalid state occured. You should do this, and you should provide as much useful information when throwing exception as you can. You should avoid, however, throwing exceptions instead of introducing checks for expected states, or treating them as just another return code (courtesy Dominic Widdows).
Throwing exceptions is costly
This argument is of less significance on modern computers, but it is worth to keep it in mind.
Throwing exceptions involves things like gathering call stack, cleaning stack from method calls when navigating up the call stack, probably involving some page-faults. More on what is the cost of exceptions in Rico Mariani’s article. “If you ever get to the point where exceptions are significantly hurting your performance, you have problems in terms of your use of exceptions beyond just the performance.”
In order to illustrate the cost of exception, I’ve written a very simple program, which sums first 10 000 numbers in the array. You can find two implementations there, please keep in mind that these are only for illustratory purposes.
First implementation naively iterates up to the limit, and skips any elements that result with exception, by simply catching a generic exception with empty catch clause. In real life scenario, it could surface as an off-by-one error in implementation. second implementation proactively checks for any out of bounds iterations. The former runs for 3 seconds, while the latter takes only 0.2 seconds. I run it 10 times to get statistically significant sample, and I run it in release mode. you can find source code here: https://github.com/psla/trycatch
Please note, that the first implementation might have another sideeffect. If the sum was done in a checked manner (checked(sum + data[i]);), the first implementation would return incorrect result while second would end up with System.OverflowException, if sum of numbers would exceed integer range.
Throwing exceptions makes debugging harder
Pretty often we, developers, spend time debugging. My debugger is set to catch first-chance exceptions, because any exception indicates, in my opinion, improper behavior of application. In Visual Studio you can set up your debugger to stop on first chance exceptions by going to Debug->Exceptions
Whenever your debugger is set up like this, it stops on all thrown exceptions. This why I don’t have to set up a break point if I want to stop on the exception and I can easily find all possible problematic cases (it will stop your debugger even when someone catched this exception and swallowed it). This is extremely useful when you are debugging code with a global try { } catch { } statement, or any other try { } catch { throw; } statements. However, sometimes it is also extremely inconvenient. If part of your code uses exception swallowing as a normal flow of program, you will get many stops in the debugger. For example in the code from previous section you might see without settings any breakpoints (just by running debugger):
Take a look at the SumSafe to see that you can actually avoid throwing exception simply by checking if the index is inside of bounds. Or you could actually rewrite the for function to do so 🙂
Swallowing exceptions leaves no clue on what went wrong
If you need to catch exception and do nothing, please at least log it. There are many libraries out there that you can use (NLog, Log4Net, Trace & Trace Listener …). Writing out information and stack trace might help you investigate issues after your application is already deployed / released to client.
Avoid catching general exceptions
Try to be as specific as you can about expected exceptions. If you know that specific code might throw specific exception catch them and only them, not the general Exception type. If you catch Exception you have no control over what you have just caught, and you cannot take appropriate action. This means, that you cannot recover, and if you cannot recover, your program should not continue. Depending on your application type, when you are left with unexpected state, unexpected values will be produced. It is very likely that it is safer to terminate than continue.
If you are unsure what to do with the exception you caught, (re)throw it. Leave original stacktrace (if you are throwing new exception, populate InnerException).
Summary
Always treat exceptions as the last resort, for things that are, ekhm, exceptional (see question here). Throwing exception breaks the program flow, and you are basically driving your application by exceptions (goto usually is a bad smell, and exceptions are not much different from goto). Exceptions are not (and should not be) a control flow mechanism.
As a bonus to this article, an interesting entry on TheDailyWTF about the code producing fewest errors by catching exceptions 🙂
Leave a Reply