Another way to get LINQ to Objects instead of a LINQ provider

Last week my SRT Colleague Chris Marinos wrote a blog post discussing the differences between F# and C# quotations. If you don’t read his post carefully, and if you read my latest article on the C# Developer Center, you may think he’s not correct in how C# resolves calls to IQueryable and IEnumerable.  He is correct, and his post shows yet another subtle rule in how the C# compiler resolves parameters.

Here’s the code that could confuse readers:

        //refactoring the predicate into a helper method
        
//NOTE: in practice, I'd probably leave this as a lambda
//since it's neither complicated nor multi-purpose.
public bool Condition(int x)
{
return x > 15;
}

var values =
0.Through(20)
.Where(Condition);

As Chris points out in his article, this will work, but will use the LINQ to Objects implementation for the Where clause.  In my article in LINQ providers, I said that you would use the AsEnumerable() method to force the LINQ to Objects implementation.  Well, we’re both right. Had Chris wrote the query this way:
        //refactoring the predicate into a helper method
        
//NOTE: in practice, I'd probably leave this as a lambda
//since it's neither complicated nor multi-purpose.
public bool Condition(int x)
{
return x > 15;
}

var values =
0.Through(20)
.Where(x => Condition(x));

That would use the LINQ to SQL implementation (based on IQueryable), and you’d have an exception thrown because the LINQ to SQL libraries has no idea how to implement a general method in T-SQL.

Did you spot the difference?

In the first case, the parameter to Where() is a Method Group. In the second case, the parameter to Where is a Lambda Expression whose right side is a MethodCallExpression. That makes all the difference. A method group cannot be converted to an expression, but it can be converted to a delegate (if the argument lists allow it). IQueryable.Where() takes an Expression<Func<T, bool> as its parameter. IEnumerable.Where() takes a Func<T,bool>. Both IQueryable.Where and IEnumerable.Where are in scope (because IQueryable<T> derives from IEnumerable<T>), and only one matches. Therefore, in Chris’ sample, the IEnumerable version gets called.

In my C# DevCenter article, I continued to use lambda expressions as the parameters to my methods. That meant when the IQueryable implementation could not do what I wanted, it would throw an exception. Then, I would add the AsEnumerable() call in order to expressly choose the LINQ to Objects implementation.

I prefer using the AsEnumerable() call because it makes your intent clear. I would not rely on passing a method group to a query method in order to force the LINQ to Objects implementation. Someone who doesn’t read closely would assume it is wrong, and change it. However, it is important to understand why sometimes your code does exactly what it is supposed to do, even when that’s not what you intended.

Created: 9/27/2010 7:09:09 PM

Current Projects

I create content for .NET Core. My work appears in the .NET Core documentation site. I'm primarily responsible for the section that will help you learn C#.

All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.

I'm also the president of Humanitarian Toolbox. We build Open Source software that supports Humanitarian Disaster Relief efforts. We'd appreciate any help you can give to our projects. Look at our GitHub home page to see a list of our current projects. See what interests you, and dive in.

Or, if you have a group of volunteers, talk to us about hosting a codeathon event.