Press "Enter" to skip to content

How to handle exceptions in C#

The three basic ways to handle exceptions in C# are:

  1. Swallow – Ignore and continue
  2. Rethrow, with the exception variable – Resets the stack trace
  3. Rethrow, without the exception variable – Maintains the original stack trace

I’ll demonstrate how to implement the different methods, why you’d want to use each one, why you wouldn’t want to use some of them, and how I decide which one to use in my code.

To demonstrate the differences, I created five functions that all call a Divide() function. In all cases, we’ll pass values into Divide() that will cause a “divide by zero” exception.

Here’s the code for the Divide() function:

private int Divide(int numerator, int denominator)
{
    return numerator / denominator;
}

Swallowing an exception in C#

If you wrap code with a “try…catch” block, and don’t do somehow re-throw the exception, this is called “swallowing an exception”. It’s like the catch block “ate” the exception, and none of the rest of the program will be aware an exception happened.

This is generally not a good idea. If an exception happens, you want to know about it, so you can fix the program or display a message to the user.

public void SwallowException()
{
    try
    {
        int answer = Divide(5, 0);
    }
    catch (Exception ex)
    {
        // The exception is ignored
    }
}

Rethrowing an exception with “throw ex”

A “catch” block has the ability to accept the exception, like a parameter being passed into a function. This is useful if you want the catch block to handle different exceptions differently. Maybe some exceptions should initiate a retry. Maybe you want to try some special failure-condition handling.

But most of the time, the rest of the program probably needs to know an exception happened in the function. This is known as “rethrowing an exception”. The exception will “bubble up” to the calling function, so it knows a problem happened.

If the catch block rethrows the exception by saying “throw ex;” (assuming “ex” is the name of the exception variable), the lowest line of the exception’s stack trace will be reset to the line where “throw ex;” is located – not where the exception really happened.

Personally, I believe this technique should never be used. I’ve never seen a time when I wanted less information about where an exception occurred. Please use the next technique instead.

public void RethrowException()
{
    try
    {
        int answer = Divide(5, 0);
    }
    catch (Exception ex)
    {
        // The exception is thrown,
        // but the stack trace is reset - losing information
        throw ex;
    }
}

Rethrowing an exception with “throw”

In the code below, the catch block re-throws the exception by using “throw;”, instead of “throw ex;” in the previous example. This method retains the complete stack trace of the exception – making it easier to find out where the exception happened.

However, if your “catch” block does not do anything except “throw;”, you can eliminate the “try…catch” block from the code. The behavior is exactly the same.

Because the ThrowException() function below does not do any additional processing in the catch block, the whole “try…catch” can be removed and replaced with the code in the NoTryCatchBlock() function. They both have the same results – letting the exception, with the full, original stack trace, bubble up to the calling function.

public void ThrowException()
{
    try
    {
        int answer = Divide(5, 0);
    }
    catch (Exception ex)
    {
        // The exception is thrown,
        // with the complete stack trace
        throw;
    }
}
public void NoTryCatchBlock()
{
    int answer = Divide(5, 0);
}

Example of when you would throw an exception

This function shows when you may want to use a “throw;” line in a catch block.

Imagine the function called a web service, which might be down, or return an error. You might want to have the function retry calling the web service, and only throw the exception if the call to the web service fails five times.

In this case, there is additional logic in the catch block. It increments a counter, waits for a second, and only throws the exception if the code in the “try” clock failed five times.

public void ThrowExceptionSpecial()
{
    bool hasAnswer = false;
    int retries = 0;
    while (!hasAnswer)
    {
        try
        {
            int answer = Divide(5, 0);
            hasAnswer = true;
        }
        catch (Exception ex)
        {
            retries++;
            // Pause before retrying
            Thread.Sleep(1000);
            // Eventually stop retry and throw the exception
            if (retries == 5)
            {
                throw;
            }
        }
    }
}

How I handle exceptions in C#

I normally only handle exceptions at the very “top” of the program. The main window (for WPF and WinForms programs) or controllers (for APIs and web services) have the “try…catch” block that log the exception information and display a nicely formatted message to the users.

This way, if an exception happens, I have the full stack trace – from the UI, all the way down to the code where the exception occurred.

It’s rare to want to “swallow” an exception. If an exception is swallowed, I’d still probably want to log it, so I could investigate why it happened and improve the code to deal with those situations in the future.

If I need a “catch” block to do something special and rethrow the exception, I always use “throw” – never “throw ex”. There has never been a time when I wanted less stack trace information.

And, if there is a “try…catch” block where the only code in the catch block is “throw;”, I eliminate the “try…catch” block. The code behaves the same, and eliminating the excess lines make the code easier to read.

    Leave a Reply

    Your email address will not be published.

    This site uses Akismet to reduce spam. Learn how your comment data is processed.