Does an IEnumerable have to use Yield to be deferred?
Here is test code which has helped me understand deferred execution and yield.
//immediate execution
public IEnumerable Power(int number, int howManyToShow)
{
var result = new int[howManyToShow];
result[0] = number;
for (int i = 1; i < howManyToShow; i++)
result[i] = result[i - 1] * number;
return result;
}
//deferred but eager
public IEnumerable PowerYieldEager(int number, int howManyToShow)
{
var result = new int[howManyToShow];
result[0] = number;
for (int i = 1; i < howManyToShow; i++)
result[i] = result[i - 1] * number;
foreach (var value in result)
yield return value;
}
//deferred and lazy
public IEnumerable PowerYieldLazy(int number, int howManyToShow)
{
int counter = 0;
int result = 1;
while (counter++ < howManyToShow)
{
result = result * number;
yield return result;
}
}
[Test]
public void Power_WhenPass2AndWant8Numbers_ReturnAnEnumerable()
{
IEnumerable listOfInts = Power(2, 8);
foreach (int i in listOfInts)
Console.Write("{0} ", i);
}
[Test]
public void PowerYieldEager_WhenPass2AndWant8Numbers_ReturnAnEnumerableOfInts()
{
//deferred but eager execution
IEnumerable listOfInts = PowerYieldEager(2, 8);
foreach (int i in listOfInts)
Console.Write("{0} ", i);
}
[Test]
public void PowerYield_WhenPass2AndWant8Numbers_ReturnAnEnumerableOfIntsOneAtATime()
{
//deferred and lazy execution
IEnumerable listOfInts = PowerYieldLazy(2, 8);
foreach (int i in listOfInts)
Console.Write("{0} ", i);
}
It doesn't have to use yield
- ultimately you can do everything that yield
does, by writing a custom enumerator (IEnumerator[<T>]
) and delaying the action until the first MoveNext()
. However, that is pretty painful to implement. Certainly if you do use yield
, the implementation is deferred by default (you can make it non-deferred by using two methods - one that doesn't use yield
, which then after accessing the data uses the other method (an iterator block) to implement the enumerator.
Frankly, writing enumerators is hard and buggy. I avoid it unless absolutely necessary. Iterator blocks are great.
A function (F1) that returns IEnumerable can return an IEnumerable computed in another function (F2), if F2 is deferred, then F1 is deferred
for example, in the following code, both F1 and F2 are deferred
public IEnumerable<int> F2()
{
for (int i = 0; i < 10; i++) {
yield return i;
}
}
public IEnumerable<int> F1()
{
return F2();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With