[LINQ via C# series]
This kind of code has been introduced again and again:
var positive = from number in source
where number > 0
orderby number descending
select number.ToString(CultureInfo.InvariantCulture);
This is called the query expression syntactical sugar, providing a T-SQL-like way to write query in C#.
The first thing to clarify is, this query expression is not recommended and I never used it in my projects.
Syntax
Since this is not a recommended coding way, I just give a brief introduction. The following slide is from a presentation from Microsoft Technical Roadshow 2007:
For the details you can take a look at the Query Expression Section of the C# Version 3.0 Specification.
Compilation
The only thing need to understabd is, the above query expression is compiled into normal method invocations:
IEnumerable<string> positive = source.Where(number => number > 0)
.OrderByDescending(number => number)
.Select(number => number.ToString(
CultureInfo.InvariantCulture));
The Where(), OrderByDescending(), and Select() are all extension methods for IEnumerable<T> type defined in the .NET 3.5 FCL:
- Since source is an integer array, and int[] implements IEnumerable<int>, so Where(), OrderByDescending() and Select() can also be invoked on int[]
- Where() returns a IEnumerable<int>, so we can immediately invoke OrderByDescending() on the return value of Where()
- For the same reason, we can fluently invoke Select()
This is called fluent interface pattern, which will be explained later.
This table shows some of the translation correspondings of the compilation:
| Query expression |
Method invocation |
| where clauses |
Where() |
| orderby clause |
OrderBy(), OrderByDescending(), ThenBy(), ThenByDescending() |
| Select clause |
Select() |
| group clause |
GroupBy() |
| join clause |
Join() |
| Multiple generators |
SelectMany() |
Details of those methods will be covered later.
Query expression vs. query method
The above 2 queries work totally the same, because the former is compiled into the latter. By roughly checking Framework Design Guidelines 2nd, I didn’t see comparison between query expression coding and normal method coding yet.
Personally, normal query methods (also called query operators in LINQ) are recommended, and I consistently use query methods in projects, instead of query expression. Here are the reasons:
- Query expression will be compiled into normal method invocation, and writing exact method invocation code helps understanding what is happening, and gets developers less confused;
- In some scenarios, query cannot be implemented by pure query expression, then query method has to come out; So for the consistency consideration, always using query method is preferred.
For the second reason, here is an example:
IQueryable<Product> results = (from product in source
where product.Category.CategoryName == "Beverages"
orderby product.ProductName
select product) // Query expression cannot do pagination.
.Skip(50) // So it has to be mixed with query methods.
.Take(10);
This looks some kind of consistency breaking. I prefer always query methods all over the project, because consistency is so prioritized.
Again, this comparison is from my personal perspective. One team’s coding conversion should fit its current members.
In the series, query methods are consistently used to explain LINQ. And query expressions will be discussed in the upcoming functional programming posts.