.NET Exceptions – throw ex is evil but throw is not that innocent

It’s general knowledge in the .NET community that you should not catch an exception (ex) and then rethrow it using (throw ex;) because it will have the side effect of resetting the stacktrace information to start at the new throw site instead of the original source of the exception.

Due to this behavior most people recommend that instead of using (throw ex;) we should simply use (throw;) which curiously gets compiled to a rethrow statement  in IL.

However throw is not that innocent as we’ll see in the following code example. We start with a fictitious Bookmark class with a Parse method that throws exceptions on invalid input.

Next we have a small test program where we’ll illustrate three ways of catching and rethrowing an exception. The first one is pure evil and it’s just here for demonstration purposes. The second one it’s the generally recommend way of doing things and finally the third shows an alternative approach that proves to have benefits in some scenarios.

using System;
using System.Collections.Generic;

class Bookmark
{
    public string Name { get; set; }
    public string Target { get; set; }

    public static Bookmark Parse(string value)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        var parts = value.Split('|');

        if (parts.Length != 2)
            throw new FormatException("Bad value format.");

        return new Bookmark { Name = parts[0], Target = parts[1] };
    }
}

class Program
{
    static string[] Args { get { return Environment.GetCommandLineArgs(); } }

    static void ParseBookmarks1()
    {
        var bookmarks = new List<Bookmark>();
        try
        {
            bookmarks.Add(Bookmark.Parse(Args[0]));
            bookmarks.Add(Bookmark.Parse(Args[1]));
        }
        catch (FormatException ex)
        {
            bookmarks.Clear();
            throw ex; // EVIL - DO NOT DO THIS
        }
    }

    static void ParseBookmarks2()
    {
        var bookmarks = new List<Bookmark>();
        try
        {
            bookmarks.Add(Bookmark.Parse(Args[0]));
            bookmarks.Add(Bookmark.Parse(Args[1]));
        }
        catch (FormatException)
        {
            bookmarks.Clear();
            throw;
        }
    }

    static void ParseBookmarks3()
    {
        var bookmarks = new List<Bookmark>();
        try
        {
            bookmarks.Add(Bookmark.Parse(Args[0]));
            bookmarks.Add(Bookmark.Parse(Args[1]));
        }
        catch (FormatException e)
        {
            bookmarks.Clear();
            throw new FormatException("Invalid command line args.", e);
        }
    }

    static void Main()
    {
        // Test 1 - throw ex;
        try { ParseBookmarks1(); }
        catch (Exception e) { Console.WriteLine(e); }
        // Prints: Line 75 and 40

        Console.WriteLine();
        // Test 2 - throw;
        try { ParseBookmarks2(); }
        catch (Exception e) { Console.WriteLine(e); }
        // Prints: Line 81, 55 and 19

        Console.WriteLine();
        // Test 3 - Wrap and throw;
        try { ParseBookmarks3(); }
        catch (Exception e) { Console.WriteLine(e); }
        // Prints: Line 87, 70, 62 and 19
    }
}

As you can check for yourself by running this example each approach will give you a different amount of debugging information present in the stacktrace. For this scenario, where we are performing several Parse operations inside the same try block where an exception is being catch, the last approach turns out the more useful because it’s the only one that clearly indicates that it was the first Parse operation to throw the exception.

Advertisements

3 thoughts on “.NET Exceptions – throw ex is evil but throw is not that innocent”

  1. Hi,

    One more interesting post. Actually, I had never thought about it.
    Because of this, I’m currently change some of my exception handling and one question came to my mind.

    On your second scenario, I think that I can replace “catch (Exception)” by “catch”, or not? Or there is another trick about that?

    1. I assume you are referring to the catch(FormatException). If so, yes you can replace it if your use case mandates it, but in this case it would change what it does and it’s there to make all three scenarios equivalent in terms of logic. They all only catch and rethrow FormatException.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s