|
| 1 | +# Query evaluation is lazy |
| 2 | + |
| 3 | +In LINQ, query evaluations are *lazy*. That means the output of a LINQ query isn't generated from the code that defines the query. Instead, it's generated when some code requests the results of the query. To illustrate this behavior, let's separate the code that generates an input sequence from the code that runs the sequence. |
| 4 | + |
| 5 | +This first block of code generates an input sequence using the numbers 1 through 9: |
| 6 | + |
| 7 | +``` cs --region generate-source-sequence --source-file ../src/Program.cs --project ../src/LINQ.csproj --session lazy-evaluation-basic |
| 8 | +``` |
| 9 | + |
| 10 | +This method is an *iterator method* and is explained in more detail below. For now, what's important is that it displays a message for each new item requested. |
| 11 | + |
| 12 | +This next block of code consumes this sequence, using the LINQ query you say on the previous page: |
| 13 | + |
| 14 | +``` cs --region consume-sequence --source-file ../src/Program.cs --project ../src/LINQ.csproj --session lazy-evaluation-basic |
| 15 | +``` |
| 16 | + |
| 17 | +Click the *Run* button to try it. Pay attention to the order of the messages. |
| 18 | + |
| 19 | +First, notice that the messages generated from creating the sequence are interspersed with the messages to display the output sequence. But more than that, notice how two elements are generated for each output message. When an even element is generated, it doesn't pass the `where` clause. The query asks for the next input element. That one does pass the filter, so its square is computed and displayed. |
| 20 | + |
| 21 | +Modify the code that generates the sequence, or the code that modifies the sequence to explore on your own. For a few examples, try the following: |
| 22 | + |
| 23 | +- Modify the generator to create all the even numbers, then all the odd numbers: |
| 24 | +```csharp |
| 25 | +for (int i = 2; i < 10; i += 2) |
| 26 | +{ |
| 27 | + Console.WriteLine($"\tProducing {i}"); |
| 28 | + yield return i; |
| 29 | +} |
| 30 | +for (int i = 1; i < 10; i += 2) |
| 31 | +{ |
| 32 | + Console.WriteLine($"\tProducing {i}"); |
| 33 | + yield return i; |
| 34 | +} |
| 35 | +``` |
| 36 | +- Modify the consumer to change which elements are filtered: |
| 37 | +```csharp |
| 38 | +where n % 2 == 0 |
| 39 | +``` |
| 40 | +- Turn that single query into multiple queries: |
| 41 | +```csharp |
| 42 | +var numbers = from n in GenerateSequence() |
| 43 | + select n; |
| 44 | +var oddNumbers = from p in numbers |
| 45 | + where p % 2 == 1 |
| 46 | + select p; |
| 47 | +var squares = from s in oddNumbers |
| 48 | + select n * n; |
| 49 | +``` |
| 50 | +- Add loops to display the output of each of those separate queries. |
| 51 | + |
| 52 | +Try your own ideas. |
| 53 | + |
| 54 | +The preceding suggestion shows two key advantages to the lazy evaluation used by LINQ. LINQ queries *compose* well. You can create queries or methods that return the results of queries. Those queries can be combined easily to perform very sophisticated algorithms. Combining smaller and simpler queries creates more readable code without sacrificing other design considerations. |
| 55 | + |
| 56 | +The other advantage to lazy evaluation and composition is that each element of the input sequence is processed in turn, and each element of the output sequence is processed in turn. That means it's not necessary to story interim results in memory. |
0 commit comments