DEV Community

Cover image for 5 Good Practices for Error Handling in C#
ByteHide
ByteHide

Posted on • Edited on • Originally published at bytehide.com

5 Good Practices for Error Handling in C#

The best way to prevent having bugs in your program or app is to avoid handling errors that could occur in your code. If you can avoid them, you won't have to check for them and handle them when they happen, right?

While that sounds good in theory, it's not always possible to completely prevent errors from occurring; otherwise you wouldn't have a program or app at all! When an error does occur and your program can't recover on its own, how you handle the error will depend on how much information the error provides about what caused it and where the problem occurred.

That is why I have decided to explain 5 simple and useful ways to handle errors in C#.


Understand what is error handling

First and foremost, it is necessary to understand what error handling means. The term error handling refers to a software application's reaction and recovery mechanisms in the event of an error. That is to say, it is a process that involves accurately predicting, detecting, and resolving errors in applications, programs, or communication.

Maintaining a program's regular execution flow is made easier via error handling. When it comes to error-handling approaches, many applications really encounter a slew of architectural issues.

🧠Remember: thrown errors are good.


Always keep trace of the exception stack

Using throw e; is not a good way to throw a caught exception since C# allows us, simply with throw;, to throw the exception in a catch block.

This way we would keep track of the stack and get a much better view of the exception.

Bad way:

try
{
    FunctionThatMightThrow();
}
catch (Exception ex)
{
    logger.LogInfo(ex);
    throw ex;
}
Enter fullscreen mode Exit fullscreen mode

Good way:

try
{
    FunctionThatMightThrow();
}
catch (Exception error)
{
    logger.LogInfo(error);
    throw;
}
Enter fullscreen mode Exit fullscreen mode

Including information in an exception is a good practice since it will help in the debugging of the error. If, on the other hand, the goal is to record an exception, then throw should be used to pass the buck to a caller.


Avoiding the use of "throw ex"

At first look, any developer could believe that throw ex is the best way to rethrow an exception after catching it. Yes, it is a simple way to do it, but not the right way to do it. This option is not good because you would lose the stack trace.

Bad way:

try
{
    // Do something..
}
catch (Exception ex)
{
    // Any action something like roll-back or logging etc.
    throw ex;
}
Enter fullscreen mode Exit fullscreen mode

Good way:

try
{
    // Do something..
}
catch (Exception ex)
{
    // Any action something like roll-back or logging etc.
    throw;
}
Enter fullscreen mode Exit fullscreen mode

We can see in the good way to do it I have simply used throw. In this way, the original exception stack would be conserved. Otherwise, with throw ex, it would be overwritten with the line of code where this statement was called.


Avoid using if conditions

You should consider using numerous catch blocks for exception handling if you need to do different actions depending on the kind of exception. A bad practice is to use if conditional. 

Bad way:

try
{
    // Do something..
}
catch (Exception ex)
{
    if (ex is TaskSchedulerException)
    {
        // Take action
    }
    else if (ex is TaskCanceledException)
    {
        // Take action
    }
}
Enter fullscreen mode Exit fullscreen mode

Good way:

try
{
    // Do something..
}
catch (TaskCanceledException ex)
{
    // Take action 
}
catch (TaskSchedulerException ex)
{
    // Take action
}
Enter fullscreen mode Exit fullscreen mode

Even if we are creating numerous catch blocks, it is always suggested to create a catch block that includes the Exception argument as the final catch block. It provides as an auxiliary catch block.


Always analyze the caught errors

If you catch an error, there's no point in ignoring it and letting it go because you won't have a chance to fix it or do anything about it.

It is always better, if you know an error might occur, to wrap that code in a try/catch.

Bad way:

try
{
    FunctionThatMightThrow();
}
catch (Exception ex)
{
    // silent exception
}
Enter fullscreen mode Exit fullscreen mode

Good way:

try
{
    FunctionThatMightThrow();
}
catch (Exception error)
{
    NotifyUserOfError(error);
    // Another option
    ReportErrorToService(error);
}
Enter fullscreen mode Exit fullscreen mode

As we can see, throwing the error is not a great solution because it might get lost in the console among all the printed stuff. By wrapping it in a try/catch, we can later (in case an error occurs there) create a plan or have a code path on how to handle it.


These tips are adapted from Clean Code Javascript. Thanks Ryan McDermott for your great contribution!

Top comments (4)

Collapse
 
canro91 profile image
Cesar Aguirre

Yes, Don't throw up; :D That's how I learned it the first time

Collapse
 
jeydotc profile image
Jeysson Guevara

I never knew about this practice, can you provide some link to read more about it? Or elaborate a bit more? I'd thank you :)

Collapse
 
lukocastillo profile image
Luis Castillo

Nice contribution!!

Collapse
 
gustavoamerico profile image
Gustavo Américo

catch (Exception error)
{
logger.LogError(error);
logger.LogCritical(error);
throw;
}