Deferred Execution

Or, not putting things back where they belong.

I am a big fan of using LINQ, but it’s not without its gotchas.  I recently wrote some code that was behaving unexpectedly, and it took me a bit to figure out what was actually going on, even though it was actually pretty simple.  (Sometimes it’s the simple things that we forget.)

So, let’s bake a cake.  Here’s a snippet.

. . .

var unusedDry = ingredients.Where(i => i.IsDry() && !i.Used);

foreach (var ingredient in unusedDry)
{
    this.Measure(ingredient);
    this.Combine(ingredient);

    ingredient.Used = true;

    this.ReturnToCupboard(ingredient);
}

this.Mix();

. . .

But let’s be honest.  No matter how well your parents taught you, no one actually puts things away immediately after using them.  Usually we do this all in one go at the end.  That’s a simple enough change:

var unusedDry = ingredients.Where(i => i.IsDry() && !i.Used);

foreach (var ingredient in unusedDry)
{
    this.Measure(ingredient);
    this.Combine(ingredient);

    ingredient.Used = true;
}

this.Mix();

. . .

await Task.WaitAll(unusedDry.Select(i => i.ReturnToCupboardAsync());

Sweet!

Or is it?  Turns out that none of the unused dry ingredients got put back where they belong.

When you write LINQ, it’s so natural that sometimes it’s easy to forget that your variable doesn’t actually contain the results of your query, but instead a data structure that contains the query itself, to be executed at some later point in time, when it needs to.  In this case, the Task.WaitAll() method executes the query that’s passed in, namely ingredients.Where(…).Select(…). Since the Used property has been set on each of the unused dry ingredients at this point in time, the results of the query are in fact empty, and Task.WaitAll() is given an empty IEnumerable<Task>.

At times like these, you can use the ToArray() or ToList() methods to force the evaluation of the query and to cache the results.

For more, see Classification of Standard Query Operators.

Post a comment or leave a trackback: Trackback URL.

Leave a comment