Attention, I will make some simplifications below, the space does not allow to talk about everything that the theme asks. And mostly I will use the words "generally" and "probably"a lot. There are no absolute truths in software development. So beware of "good practice", especially if you are like most people who forget what it means and start treating recommendations for most cases as absolute truths for all cases. Good practices tend to make people stop thinking about problems, they become a recipe for cake. Especially occurs in the comment tutorial on the question. It looks good but is not so much. In an attempt to simplify the explanation he preaches things that are usually wrong. Maybe the person who wrote it knows it, but the person reading it probably doesn’t.
I would like to know your opinion. Is there a [better | correct] way to propagate and display the exceptions simply? This is a good way to deal with Exceptions?
Getting to the point, this NAY is a good way to deal with exceptions.
Perhaps the example is not the best to indicate your intention, but I can only judge by what you posted. I would never make a code like that in production, he has some problems:
- It intercepts a very generic exception. "Good practice" is only to intercept more specific exceptions. How specific they should be depends a lot on the case. But probably catch the
Exception
is error. Catch the Exception
can be useful at a high level of call stack (in the Main()
for example).
- He launches a
Exception
. The class Exception
should be abstract (ok, I know this would create some undesirable limitation in certain cases) to avoid someone instantiating and releasing something that was actually made to be an abstraction.
- It destroys the stack trace and it becomes more difficult to find useful information, when the goal seems to be opposite.
- It does not provide any semantic improvement except for the original, it only puts a little dress and lipstick on it (and looks like a transvestite :) ). Particularly it is not specializing anymore.
- He manipulates an exception without knowing what to do with it.
- He’s letting an exception occur when it can be prevented. I know it’s just an example, but I’m doing the analysis of what exists. Bad examples make people misunderstand things.
If you’re wearing one throw
without being immediately followed by a ;
, you’re probably doing something wrong. Preserve the stack trace is so important that C# 6 now has Filtered Exception without which the new compiler, part of the Roslyn compiler, could not have been created properly.
try { … }
catch (MinhaException e) if (meufiltro(e)) {
…
}
Capture Exception
, ApplictionException
, SystemException
, etc. are too generic to be able to do anything useful. Are exceptions that usually should only be captured as the last action before the application/thread break. Almost always the executed action will be log in the state before termination. Note that treatment of exceptions in multi application-thread are even more complicated. There are several other concerns to correctly manipulate in a parallelism environment that do not come to the case here.
Not knowing clearly what to do with an exception, so without having a real solution to the problem, an exception shall not be caught. Keep in mind that 80% of programmers make mistakes in handling exceptions 80% of the time. Okay, numbers get kicked, but even experienced programmers make mistakes because they don’t understand deeply the topic that generated the exception (not just the exception itself). Some people would even say what exceptions are abstraction leak.
Centralize the capture of exceptions, especially at a high level of call stack that you have knowledge or ability to handle properly is a "good practice". Writing in log and eventually the notice to the developer/application support is the most common action in these cases. When using threads, there is a central point for each thread.
When you’re making exceptions, think twice. They are slow to launch and make it difficult to debug code. And often programmers don’t know well when the situation really is exceptional or when it’s just an alternative flow to the program. The situation worsens when one tries to choose the right specificity and semantics for the failure. Therefore, capturing and trying to improve well-architected exceptions is likely to result in defeat. Use existing exceptions unless you add something really useful to your own exception. Read the .’s source code. NET to understand where and when to release existing exceptions.
Prefer whenever possible to treat the fault before the exception occurs. Have a method TryAlgumaCoisa()
avoids the use of the exception. Of course, this is not always possible. Can’t use this technique when you need more than "failed/worked", when there is risk of race condition, etc..
If really need to intercept an exception, try to launch it again fully with throw;
and if you still have to cast the same improved or more specific exception, use the Overload which preserves the stack trace (Ex.: Exception(String, Exception)
). If the exception you’re going to cast doesn’t have that Overload, run away from it then, it was done very dirty and not worth its use.
If you need to "decorate" the exception messages before presenting to the user, do so only when you are actually manipulating the exception. Scan the stack trace and other members of the exception and turn the messages immediately before the presentation. Do not embellish the message before, contrary to what the example shows.
Be careful what you put in the exception messages. I almost die laughing with some web applications that just need to include the root password for everyone to see.
Logue ex.ToString()
and not ex.Message
.
Some common mistakes your example does not make:
The code is not swallowing the exception altogether.
It does not pose a very complicated logic in the catch
, otherwise you start running the risk of having an exception while handling another exception. Simplify! Just do what you need to do.
Not everything should be in one catch
, the finally
may be more appropriate in some situations.
Most exceptions in treating exceptions (sorry for the pun) will occur in situations where you are creating one framework or an activity with a lower level of abstraction. In these cases you either know what you’re doing, you have to know, or you deserve all the suffering you’ll have :)
There is the event Application.ThreadException
which is a safety net in case all attempts to capture an error fail. It’s a very limited resource where basically you’ll only get log in the current state. Do not try to use without having a deep understanding of your commitments.
Compile your example and other Compile by removing all error handling. Then you tell me which one provided the most useful information. You’ll probably be surprised. Compile with debug.
What is the importance of capturing more specific Exceptions, for example: DivideByZeroException
?
It has been said before that it is almost always best to capture more specific exceptions. You will probably have better conditions to handle the exception properly.
Especially the DivideByZeroException
it must not be handled, at least not under normal conditions. I will explain further below but the basic reason that it should not be captured, except for some reason that determines otherwise, is that either there is no way to recover from this failure or it can be prevented before the exception occurs.
If you can do something to save a calculation after receiving one DivideByZeroException
then you know that the splitter will be zero and should not perform this calculation.
Exceptions should not be used for application flow control or determine business rules.
I really like the article by Eric Lippert on the subject and I will make a summary here.
Type |
What to do |
Example |
Fatal |
Nothing, CLR will know what to do better than you |
Outofmemoryexception |
Mess |
Fix the bug that’s causing this |
Argumentnullexception |
Noisy |
a) Fix poorly architected software/framework b) Poorly architected library provider switch c) Accept the injury and treat it as if it were foreign |
Luckily I know no example |
Foreigner |
Manipulate in the most appropriate way possible |
Filenotfoundexception |
There are 2 groups of exceptions: recoverable and unrecoverable.
As recoverable are divided into two types. One clearly must be manipulated whenever possible and the other too, but only if you cannot escape from it.
The first type is represented by the exceptions foreign (exogenous). They occur due to unmanaged resource failures. They are the result of failures in trying to access external resources controlled by the operating system or processes that serve its application. Errors produced by filesystem, network, database, application server, etc. In general you can not only handle properly, but probably should. It will probably define whether it will retain, whether it will terminate the resource in use, whether it will warn and ask what to do for the user, whether it will try an alternative, anyway, the creativity can be great here. Capturing as close to the point of exception as possible is usually a good idea, although in some cases you can generalize actions.
The second type is the exception that exists for noise (vexing). It is the exception that should not exist. It exists because it was poorly architected. If it was created by you or your team, find a way to provide an alternative way to treat an event that is not exceptional. If this occurs in a third-party library, try to have the vendor give you an alternative; or think about abandoning it. Besides not understanding how exceptions should be created, is he still stubborn? Run away from him. Can’t you? Well, then do what the documentation says or find out for yourself what to do (documentation is still missing? really run away). Yourself. NET has already had some examples of exceptions created mistakenly, but today there are alternatives to avoid its use.
The other group, as its name says, are unrecoverable. At first understand: do not try to recover these exceptions! Common mistake of beginner, even of beginners with 10 years of experience.
The first type are the flaws fatal or environment. There is nothing you can do in these cases. Feel lucky if you can log in error, besides leaving the message a little more cute or palatable to the user.
The second type is where people make the most mistakes in manipulation, and it should be the easiest. It’s the mess (boneheaded). These are programming errors. It’s the fault of the developer. But for some reason (maybe Freud explains) the programmers think they can and should solve the problem in the run (change the tire with the car walking). Very rarely can, but even when they can, should not try to recover from it. The only way to deal with in programming error, is to fix it. Of course during the program you can do something and this thing is at most to make the message cuter, log in and alert the developer. The way to handle these exceptions may vary whether you are developing or in production. That kind of mistake should never come into production, but it does. Pretending that the error did not occur or trying to do something to keep the program running can only bring more problems. And the place to do this is on Main() or near it. Hardly an exception of this kind should be intercepted before.
DivideByZeroException
is an example of programming error! And it’s amazing how many programmers think it’s not. There are cases that you try to check the splitter before and then do the calculation can cause a race condition. In these cases, start all over again, you have some serious problem in the architecture of your application.
Some more modern languages are even using different mechanisms to deal with failures. They provide a mechanism to give a personalized and centralized output in unrecoverable cases and another mechanism to treat recoverable cases in a simpler way than a try catch
. Interestingly I know a very old language that already realized that they should have 2 simple mechanisms instead of exceptions :)
See also Why should we avoid returning error codes?.
I realized that some people don’t agree with everything here, maybe these people understand C# more than Eric Lippert.
I found a tutorial VERY interesting!!! Here: http://www.codeproject.com/Articles/125470/Exception-Handling-for-C-Beginners
– Reiksiel