Best way to deal with Exceptions

Asked

Viewed 9,003 times

55

During my work I learned a way to deal with Exceptions, but I don’t know if it’s very good. Here’s an example of the code:

class Program
{
    private static void Main(string[] args)
    {
        try
        {
            Foo();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message + Environment.NewLine + (ex.InnerException != null ? ex.InnerException.ToString() : String.Empty));
        }
        Console.ReadKey();
    }

    public static void Foo()
    {
        try
        {
            Bar();
        }
        catch (Exception ex)
        {
            throw new Exception("Erro no método Foo: " + Environment.NewLine + ex.Message + (ex.InnerException != null ? ex.InnerException.ToString() : String.Empty));
        }
    }

    public static void Bar()
    {
        try
        {
            DateTime? data = null;
            int x = data.Value.Day;//forçar uma exception de proposito
        }
        catch (Exception ex)
        {
            throw new Exception("Erro no método Bar:" + Environment.NewLine + ex.Message + (ex.InnerException != null ? ex.InnerException.ToString() : String.Empty));
        }
    }


}

My idea is to map the errors and play the responsibility of your treatment to the main method.

To be more objective I’ll direct the questions:

  1. 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?
  2. What is the importance of capturing Exceptions more specific, for example: Dividebyzeroexception?
  • 2

    I found a tutorial VERY interesting!!! Here: http://www.codeproject.com/Articles/125470/Exception-Handling-for-C-Beginners

4 answers

52


The question is great, but I’m going to try an answer that is not based on opinions, because the focus of Stack Overflow is not the discussion.

There is a way to [better | correct] simply propagate and display exceptions?

It depends on the interface of your application. For example, in a web application, the best way to deal with an exception is by scaling it, displaying on-screen warnings with the exception informative message or using a logic that deals with the error internally, triggering some additional logic or reporting the problem in log.

What is wrong is to stifle the exception, preventing it from its normal behavior, which is to interrupt the execution of normal code flow to avoid larger problems in the system.

In the case of your code, if the intention is to record the exceptions in Console, the code is correct and adopts good practices. In this case, you are accumulating the exception messages, which is correct, and displaying in an output that does not appear to the user. In this case, it is only important to return to the user a readable and understandable message of the problem occurred.

What is the importance of capturing more specific Exceptions, for example: Dividebyzeroexception?

Certain situations can lead to different errors, which requires different treatments. For each treatment, the typification of the exception is a good practice to provide to each situation of error the appropriate treatment.

49

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:

  1. 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).
  2. 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.
  3. It destroys the stack trace and it becomes more difficult to find useful information, when the goal seems to be opposite.
  4. 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.
  5. He manipulates an exception without knowing what to do with it.
  6. 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.

13

Regarding Exceptions in . Net you need to understand the following:

Try/Catch

Try/Catch means: Try, and Treat. If your code does not have a specific treatment for the exception, do not treat it. But this does not mean that your exception should not be handled by anyone, only that the current method (which may be on a 10-level call stack) should only have a Try/catch if there is some sense to catch.

Try/Finally

Try/Finally Try/Finally means: Try, and the final... If you have an operation that needs to be performed even if it is an error, use it.

Idisposable, Using and Try/Finally

When you implement Idisposable, you can use the using for the creation of its object. What people don’t know is that Using is an implementation of Try/Finally where in Finally the Dispose() method is called.

Creating your own exceptions

It is recommended that you segment your exceptions using inheritance, creating a specific inheritance branch for business exceptions. This segmentation makes it easier to make a decision about what should be done with Exception. For example:

Business exceptions are sent to the user in the form of a popup, or are simply logged in as Warning and not Error. In counterpart, the other exceptions are treated as error, and perhaps deserves a generic message to the user.

Create at least one Exception, which represents the Exception of business. Make the most of the exceptions already contained in the . Net Framework.

Heed When you start building your exceptions, you naturally and unconsciously start to look for every answer (negative) validation as a specific potential Exception. In practical, not necessary! Create as many exceptions as necessary to facilitate treatment. If a special Exception does not have specific treatment, or you expect someone to treat you specifically one day (any consumer of your class) or you You’re making too many exceptions. It is common that they tend to create double exceptions with the exceptions of . Net Framework such as Argumentexception, Argumentnullexception etc. No make that mistake. One thing is for a programmer to call a method, passing an invalid parameter, another thing is that you cannot "save an object" because it is in a state (in the business vision) that does not allows this type of operation.

Throw and Re-throw

  • If you need to launch an Exception, do it!
  • If you need to release an Exception from a catch of a Try/catch, then put the original Exception as innerException.
  • If you need to treat an Exception and relaunch it, use throw;

The difference between rethow:

WRONG

try
{
    ...
}
catch(Exception ex)
{
    ...
    throw ex;
}

CORRECT

try
{
    ...
}
catch(Exception ex)
{
    ...
    throw;
}

The difference is significant and is not subject to interpretation. The first example, wrong, generates a change in Exception’s Stacktrace, causing you to lose Exception traceability. In the second example the behavior is to relaunch the same Exception, in the same way that it came to you.

Global Treatments

Only the most external points of your API, service, etc., should effectively handle exceptions maybe omitting them for the user/consumer and always logging in.

Solutions for global treatments:

It has already been said here in another answer, the treatment for the current thread, but you can treat also in Appdomain. Using aspects (AOP) is also an excellent alternative. Treatment for WCF needs to be differentiated, as exceptions in WCF cause channel breakage, so in this scenario, your exceptions need to turn fouls.

10

Complementing good answers, but something more advanced below.

The . Net provides two subclasses of Exception, little known: Applicationexception and Systemexception.

Systemexception is already used by the framework for native exceptions, programmers should, especially when using DDD, create their exceptions and derive from Applicationexception.

Because it is simple, you gain more granularity, you can easily discern whether it was an exception generated by user programmer or system, scale the error from a low-level layer up in a controlled way.

Capturing only Exception is a bad technique, it should be used as failsafe, the ideal is to capture the specific exceptions and give the correct treatment. A good programmer knows which exceptions are thrown from the protected code chunk, otherwise he won’t know how to handle it.

try
{
    // exception code
}
catch (FileNotFoundException ex) { // tratamento para arquivo não encontrado }
catch (ArgumentOutOfRangeException ex) { // tratamento para acesso incorreto de indice }
catch (CustomException ex) { // tratamento para exceção que eu criei }
catch (Exception ex) { // Algo inesperado ocorreu !!!!, log e analise }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.