A LINQ Tutorial
As part of my on-going exploration of technologies related to programming Silverlight and Windows Phone, I’ve been exploring LINQ in some detail.
Today, I’d like to take a look at how LINQ queries are executed; specifically focusing on the delay in execution that makes LINQ so performant.
[Double click on image for full size]
To begin, divide your main page vertically into two equal columns, and add a ListBox to each column, as shown in the following Xaml,
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <ListBox Name="DeferredLB" Grid.Column="0" Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> <ListBox Name="NotDeferredLB" Grid.Column="1" Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> </Grid>
To demonstrate that LINQ uses deferred execution, we’ll set up a query that relies on a method call. Inside the method call, we can add to the ListBox so that we can see which method is running at any given moment.
public MainPage( ) { InitializeComponent( ); int[] primes = { 1, 2, 3, 5, 7, 11, 13, 17, 19 }; var query = from prime in primes select Doubler( prime, DeferredLB ); foreach ( var n in query ) DeferredLB.Items.Add( n ); } private static int Doubler( int n, ListBox lb ) { lb.Items.Add( "Doubling " + n ); n *= 2; return n; }
When you run this application, you see that control bounces back and forth from the query in Main to the Doubler method as each item is yielded in the enumerator that is implicitly called by the foreach loop. The query is not executed until needed and if you were to cut off the query half way through then the remaining values would never load into memory.
To nail this down, let’s force the query to be executed all at once. To do this, all we do is to convert the query to a list. This forces the query to execute in full (to populate the list) and then we can iterate through that list all in one go. We’ll populate the right hand list box with the results of that approach:
public MainPage( ) { InitializeComponent( ); int[] primes = { 1, 2, 3, 5, 7, 11, 13, 17, 19 }; var query = from prime in primes select Doubler( prime, DeferredLB ); foreach ( var n in query ) DeferredLB.Items.Add( n ); query = from prime in primes select Doubler( prime, NotDeferredLB ); foreach ( var n in query.ToList( ) ) NotDeferredLB.Items.Add( n ); }
The result is shown in the image at the top of this posting.
Note that we use an array and the compiler optimizes enumeration of arrays, but that does not affect the point made in this discussion.
4 Responses to Deferred Execution in LINQ