Geeks With Blogs
New Things I Learned

Got bit by a bug caused by me trying to do stuff smartly in Linq.  Gentle reminder that Linq expressions are always recalculated.  The bit of code is the following:

public static void AddKnownTypes(IEnumerable<Type> knownTypes)
{
   // We only want to collect types that are new (hasn't been registered yet)
   IEnumerable<Type> newTypes = knownTypes.Where(t => !_knownTypes.Contains(t));
   _knownTypes.AddRange(newTypes);

   // Fill in a dictionary of these newly added types
   foreach (Type type in newTypes)
   {
      _types[type.Name] = type;
   }
}

Looking at it, the code seems to be fairly benign; the method accepts a list of types, we only wanted the new ones, so filter using the Where extension method, and then add it to our list and also fill in a dictionary of types.

The bug with the above code is that the foreach body will not be entered (regardless of the types contained in knownTypes or _knownTypes).

In Linq, any queries have its execution deferred; in the case above, the newTypes variable actually has the expression as to what needs to be done.  When calling AddRange, we pass in newTypes as the parameter, and the query is executed then and there.

However, in the foreach, newTypes is still the same expression, so when it gets to that foreach line, the expression is re-evaluated again.  Guess what – at that point _knownTypes already contains all the types in newTypes , thus the query will return an empty set.

We always think of using a variable to store a value and that value doesn’t change (unless we change it or some other code change it).  In this case, it’s actually like a delegate call, which will basically execute a method that will return the value.  I’ll be sure to remember this so I don’t get bit by this again.

Edit:

My friend Kevin Grossnicklaus aptly pointed out that Linq operators fall into 2 groups, the ones that have deferred execution and the ones that are not.  For instance Sum, Count and just about any of the Enumerable extension methods that return an actual concrete type (ToList and ToArray are 2 other examples) will actually execute the query then and there.

It's when the method returns an IEnumerable<T> then the query gets deferred; which can lead to some frustrating bugs.  I'll illustrate some of these bugs in future post .

Posted on Wednesday, September 23, 2009 1:50 AM | Back to top


Comments on this post: Linq == Deferred Execution == Always Re-execute

# re: Linq == Deferred Execution == Always Re-execute
Requesting Gravatar...
Saved I do not know how may hours. Totally on time.

Big Thanks You.
Left by Biswanath on Sep 25, 2009 3:16 AM

Your comment:
 (will show your gravatar)


Copyright © Muljadi Budiman | Powered by: GeeksWithBlogs.net