Julien Roncaglia has introduced in three examples absolutely crystal explanation, I can only quote:
Considering a function
int Computation(int index)
Immediate execution
IEnumerable GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for (int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
return result;
}
When the function is called Computation is executed maxIndex + 1 times
GetEnumerator return a new instance of the enumerator doing nothing more.
Each call to
MoveNext put the the value stored in the next Array cell in the Current member of the
IEnumerator and that's all.
Cost: Big upfront, Small during enumeration (only a copy)
Deferred but eager execution
IEnumerable GetComputation(int maxIndex)
{
var result = new int[maxIndex];
for (int i = 0; i < maxIndex; i++)
{
result[i] = Computation(i);
}
foreach (var value in result)
{
yield return value;
}
}
When the function is called an instance of an auto generated class (called "enumerable object" in the spec) implementing
IEnumerable is created and a copy of the argument (maxIndex) is stored in it.
GetEnumerator return a new instance of the enumerator doing nothing more.
The first call to
MoveNext execute maxIndex+1 times the compute method, store the result in an array and
Current will return the first value.
Each subsequent call to
MoveNext will put in
Current a value stored in the array.
Cost: nothing upfront, Big when the enumeration start, Small during enumeration (only a copy)
Deferred and lazy execution
IEnumerable GetComputation(int maxIndex)
{
for (int i = 0; i < maxIndex; i++)
{
yield return Computation(i);
}
}
When the function is called the same thing as the lazy execution case happens.
GetEnumerator return a new instance of the enumerator doing nothing more.
Each call to
MoveNext execute once the Computation code, put the value in
Current and let the caller immediately act on the result.
Most of linq use deferred and lazy execution but some functions can't be so like
sorting.
Cost: nothing upfront, Moderate during enumeration (the computation is executed there)
To summarize
Immediate mean that the computation/execution is done in the function and finished once the function return. (Fully eager evaluation as most C# code does)
Deferred/Eager mean that most of the work will be done on the first
MoveNext or when the
IEnumerator instance is created (For
IEnumerable it is when
GetEnumerator is called)
Deferred/Lazy mean that the work will be done each time
MoveNext is called but nothing before.
Parallel LINQ does it a little differently as the computation could be considered deferred/Lazy from the point of view of the caller but internally the computation of some number of elements begin in parallel as soon as the enumeration begin. The result is that if the next value is already there you get it immediately but otherwise you will have to wait for it.
Source:
stackoverflow.com/a/2515920