A LINQ Tutorial
I interviewed Bill Wagner for Yet Another Podcast while we were both at CodeMash (look for his interview to be published next week). In addition to being one of the nicest guys I know, he is also one of the smartest.
During the conversation he illustrated the ideas behind Linq and Fluent programming with a verbal example. With his permission, I’ve picked up that example and present it here as a crisp crystallization of these key programming concepts.
To get started, let’s create a Windows Phone project, and on Main Page we’ll add three columns, each occupied by a ListBox control. Here’s the Xaml:
<!--ContentPanel --> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <ListBox x:Name="LB1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0" Margin="5" /> <ListBox x:Name="LB2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="1" Margin="5" /> <ListBox x:Name="LB3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="2" Margin="5" /> </Grid>
We’ll fill the three list boxes with values in three different ways:
- Imperative Programming
- Query syntax
- Fluent syntax
Imperative Programming
The imperative example is immediately understood by anyone who has been programming in C# for any length of time,
private void CreateCollections() { var someData = Enumerable.Range(1, 50); var inLoop = new List<int>(); foreach(var n in someData) if (n > 5) inLoop.Add(n * n); LB1.ItemsSource = inLoop;
This is C# code that we live and breathe and it takes virtually no explanation. It feels natural, especially to those who have been doing imperative programming in one way or another since we were wee youngin’s. (This is the essence of programming in BASIC, Pascal, C, C++, C#, etc. etc.)
Turning to Query Syntax using LINQ
The second example has become more common over the past few years and it is hard to escape the Query syntax in reading about Silverlight/ Phone/ C# programming. The syntax is very similar to SQL, except that the select statement is at the end.
var queryResult = from n in someData where n > 5 select n * n; LB2.ItemsSource = queryResult;
I read this as (roughly) “for each value in someData, find all the values where the value is greater than 5 and then return the square of that number to queryResult, which will be a collection.”
A little odd, a little declarative, but if you’ve been working with SQL Server (and who hasn’t?) it isn’t brand new.
Fluency in Fluent Programming
Just when you thought it was safe to go back in the water, the folks who think a lot about programming idioms and patterns have begun to suggest that Fluent programming can make for more understandable (read: easier to maintain) programming.
The essence of fluent programming is that the output of one operation (or method) is the input to the next, creating a string of operations that together make for a very readable coding “sentence.”
In this case, we modify the Query statement using Lambda expressions,
var fluentResult = someData .Where(n => n > 5) .Select(n => n * n); LB3.ItemsSource = fluentResult;
Interpreting Lambda Expressions
There are two approaches to interpreting the lambda expression
Where(n=> n > 5)
One perfectly legitimate interpretation is to translate this in your mind to method syntax, where the value before the goes-to operator (=>) is the parameter, and the value to the right of the goes-to operator is the body of the method. This would amount to something like:
where ( IsGreaterThan5(n) ) //... private bool IsGreaterThan5(int n) { return n > 5; }
Another way to interpret this is simply as “return those values where the value is greater than 5”
In either case, the result of that filter is passed on to the statement to the right; that is only those values greater than five will be handed to the select. It, in turn can be interpreted as “each value returns the square of that value.”
Thus, I read the entire statement as “assign to the collection fleuntResult the values that are found by starting with the collection someData and finding all the values where n is greater than five and then returning the square of those numbers.”
Thanks again to Bill Wagner who provided the code. Any mistakes in understanding or interpretation are my own.
Zhao Qiang? That policeman shouted sound.
This really is my 1st time i visit here. I located so several entertaining stuff inside your blog, especially its discussion. From the tons of comments on your posts, I guess I am not the only one having all the enjoyment here! Keep up the outstanding work.
very nice post I must say, love linq, love the lambda expressions though it’s not my first choice when programming.
@Hoytster
Dang, my tags got encoded way. Insert [FLAME ON] at the top and [FLAME OFF] at the bottom, please.
I know you’re old enough, Mr. Liberty, to remember when people would surround their rants with tags like:
What about clarity?
The simple examples here are easy enough, but as a long-time developer accustomed to the imperative style, I often find LINQ / fluent syntax incomprehensible. How do you break it down, so you can understand it? When I am struggling to understand imperative code, I’ll step through it in the debugger — but that doesn’t work with a LINQ or fluent statement, which is executed as a single expression.
What does the bizarre syntax get us? I see few advantages to non-imperative code other than showing that you’re clever and up-to-date. It may occasionally be more concise. Sure, sometimes you can write LINQ that would be complicated to do imperatively, e.g. aggregates.
Those advantages are often overshadowed, however, IMO, by the difficulty of understanding the code. Many (most?) of us spend the bulk of our time reading someone else’s code, trying to figure out how it works so we can fix or extend it. LINQ / fluent makes it harder to get the work done, because it’s tricky and obscure. It slows development.
I think that some of the fondness for LINQ / fluent syntax comes from the same impulse as the commonplace refusal to comment code. “My code is so damn clean that if you don’t get it without comments, then you’re a moron — not remotely as smart as me.” Never mind that the developer hacked at it for an hour before finally getting it work work with a solution he found on a web site. Here it is, without the helpful commentary from its StackOverflow origin, or a link back to SO.
And we’re supposed to understand it, at a glance — and who cares if we don’t? The original developer basks in the light of our bewilderment. If it’s hard for us, that makes him superior. That’s why he spent an hour figuring out how to apply LINQ to something he could have done in two minutes using imperative code.
LINQ and fluent syntax are the Emperor’s New Codes. We’re afraid to say it’s difficult because that makes us look dumb.
My personal code standard: Resort to LINQ / fluent syntax only when it makes the code cleaner and more comprehensible to the average developer. Those occasions are rare.
Note on performance: Instead of looking at IL, loop through the code and see how long it takes to execute. 99.999% of the time, you’ll need to loop a million times to get a large enough time interval to measure. In other words, the performance difference is too small to be relevant.
The time that counts is the time the next developer wastes trying to understand and debug your clever LINQ / fluent code.
Clever isn’t.
Resharper has a feature, where it will propose to convert your mundane imperative code to LINQ. Cool, I thought! I can appear clever too! 🙂
What I really want, though, JetBrains — is the opposite. You guys are way smart. Can you please give us a LINQ-to-imperative converter?
I personally don’t have a favorite between Linq and fluent, however, I find it easier to get my teammembers to write Linq (as they have an intense SQL background). For some of them the Lambda expression is hard to read and nearly impossible to write. I can’t see them comming up with a syntax like presented here.
I also have the same question, is there a performance hit? If someone is wondering how it works with custom objects:
9EC44631-AB34-4287-B3DD-0027F24E8133
ABC
var resource = Application.GetResourceStream(new Uri(@”MyMobile;component/xmls/Customers.xml”, UriKind.Relative));
StreamReader streamReader = new StreamReader(resource.Stream);
var customers = data.Descendants(“Customer”)
.Where(c => ((string)c.Element(“Name”))[0] == ‘a’
|| ((string)c.Element(“Name”))[0] == ‘A’)
.Select(c => new Customer()
{
Name = (string)c.Element(“Name”),
CustomerId = (Guid)c.Element(“CustomerId”)
});
@Jon
Web post is up with performance results. Enjoy!
http://weblogs.asp.net/bsimser/archive/2011/01/27/imperative-vs-linq-performance-on-wp7.aspx
@Jon
Okay, results done. Just writing up a short blog post. Will post link here for people interested.
great demo and it is very clear the differences. In many cases I have found myself writing methods that instead of returning void, return the current instance so I can “chain” things together in a fluent way.
@Bil Simser
Please send in the results!
@Jon
Correct. LINQ is turned into its fluent expression at the IL level. Just as a quick glance (not surprising) the imperitive code is double the number of instructions as the LINQ/fluent code. Now onto performance tests.
I find the Fluent form more readable. I looks like F# where you pipe with Seq.map for example. I think you can see more clearly the flow of information.
Just to be sure, in the last example, shouldn’t it be Where(IsGreaterThan5) ?
@Bil Simser
When Query Syntax is compiled down (to IL I believe) it actually converts it to Fluent Expressions so you should measure Fluent vs Imperative
This is a great article.
It gives names to code styles that I have been using and not known for years but also introduces beginners to the different code patterns in C#.
The question is what’s the performance difference. If it’s just about what syntactic sugar you’re comfortable with, that’s one thing. If one has better performance over another as you scale up that’s more interesting. Going to do some ILDASM on this to see if there’s any diff.