这是indexloc提供的服务,不要输入任何密码
Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions LINQ/docs/lazy-evaluation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Query evaluation is lazy

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.

This first block of code generates an input sequence using the numbers 1 through 9:

``` cs --region generate-source-sequence --source-file ../src/Program.cs --project ../src/LINQ.csproj --session lazy-evaluation-basic
```

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.

This next block of code consumes this sequence, using the LINQ query you say on the previous page:

``` cs --region consume-sequence --source-file ../src/Program.cs --project ../src/LINQ.csproj --session lazy-evaluation-basic
```

Click the *Run* button to try it. Pay attention to the order of the messages.

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.

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:

- Modify the generator to create all the even numbers, then all the odd numbers:
```csharp
for (int i = 2; i < 10; i += 2)
{
Console.WriteLine($"\tProducing {i}");
yield return i;
}
for (int i = 1; i < 10; i += 2)
{
Console.WriteLine($"\tProducing {i}");
yield return i;
}
```
- Modify the consumer to change which elements are filtered:
```csharp
where n % 2 == 0
```
- Turn that single query into multiple queries:
```csharp
var numbers = from n in GenerateSequence()
select n;
var oddNumbers = from p in numbers
where p % 2 == 1
select p;
var squares = from s in oddNumbers
select n * n;
```
- Add loops to display the output of each of those separate queries.

Try your own ideas.

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.

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.
2 changes: 2 additions & 0 deletions LINQ/docs/query-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ select n * n;
Every query ends with a *projection* that produces either a new sequence, or a single value. This first example produces a sequence where every element is the square of the input sequence.

You can modify any of these sections in the query and run it yourself.

**Next: [Lazy evaluations &raquo;](./lazy-evaluation.md) Previous: [Home &laquo;](../README.md)**
1 change: 1 addition & 0 deletions LINQ/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ You can explore LINQ in this interactive tutorial. Each lesson teaches a new LIN
## Lessons in this tutorial

- [LINQ Query Syntax](docs/query-syntax.md)
- [Understand query evaluation](docs/lazy-evaluation.md)
- More in upcoming PRs
28 changes: 28 additions & 0 deletions LINQ/src/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

Expand All @@ -25,6 +26,8 @@ static int Main(
return region switch
{
"query-syntax" => QuerySyntax(),
"generate-source-sequence" => ConsumeSequence(),
"consume-sequence" => ConsumeSequence(),
_ => throw new ArgumentException("A --region argument must be passed", nameof(region))
};
}
Expand All @@ -43,5 +46,30 @@ internal static int QuerySyntax()
return 0;
}

internal static int ConsumeSequence()
{
#region consume-sequence
var sequence = GenerateSequence();
var squaresOfOddNumbers = from n in sequence
where n % 2 == 1
select n * n;

foreach (var number in squaresOfOddNumbers)
Console.WriteLine(number);
#endregion
return 0;
}

#region generate-source-sequence
internal static IEnumerable<int> GenerateSequence()
{
for (int i = 1; i < 10; i++)
{
Console.WriteLine($"\tProducing {i}");
yield return i;
}
}
#endregion

}
}