Smuggling exceptions out of try-catches with IEnumerable

Posted on |

The code in this post is from a talk by Erik Meijer, I claim no credit.

Take a look at this screenshot:

Exception thrown from try-catch

Pretty wacky huh? Visual Studio has broken into debug mode even though the division is clearly wrapped in a try-catch. Take a guess at what’s going on before reading on.

Here’s the full code listing:

static void Main(string[] args)
{
    var xs = new[] { 1, 2, 3, 0 };
    IEnumerable q = null;

    try
    {
        q = from x in xs select 1 / x;
    }
    catch { }

    foreach (var z in q)
    {
        Console.WriteLine(z);
    }
}

The obvious expectation is that division-by-zero will be caught by the try-catch. Alas, the division does not happen in the scope of the try statement. Instead, due to the deferred execution paradigm of IEnumerable, it happens on a by-need basis in the for-loop. From there the exception can propogate unopposed to the top af the stack. Haskell, where lazy evaluation dominates, avoids exceptions (and other messy side effects) alltogether, partly for reasons demonstrated by this example.

UPDATE: As Wayne points out, fixing this is as easy as extending the try to cover the enumeration. The point of this post was mainly to poke fun at the Visual Studio debugger and to point out that lazy evaluation can bite you if you’re not careful.

Comments

Antoine on

In my Haskell interpreter:

> 1 `div` 0
*** Exception: divide by zero

For working with exceptions in Haskell:

http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html

Reply

Jonathan Allen on

You know, 99% of the time you don’t want to hold onto the query anyways. Evaluating it as a list as soon as possible is almost always the right thing to do, especially if there is a chance you need the data more than once.

Reply

Wayne on

You wouldn’t write code like that if you understood that your query is actually evaluated on enumeration, not on assignment.

you would write:

try
{
q = from x in xs select 1 / x;

foreach (var z in q)
{
Console.WriteLine(z);
}
}
catch{ }

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *