Loading…

C#: IEnumerable, yield return, and lazy evaluation

Don't hate, iterate.

Article hero image

Let’s talk about one of my favorite .NET features: IEnumerable.

This interface enables iterating over a collection. In other words, if something is an IEnumerable, you can mostly think of it like an array or a list. You can use a foreach statement to loop through it, you can use LINQ to map or reduce it in a hundred different ways, or you can explicitly cast it to an array with .ToArray() and access elements by index. But there are a few things that make IEnumerable special—and a few things that make it tricky.

IEnumerable is the return type from an iterator . An iterator is a method that uses the yield return keywords. yield return is different from a normal return statement because, while it does return a value from the function, it doesn’t “close the book” on that function. The next time a value is expected, the function will continue executing statements after the yield return until it hits another yield return (or a yield break, or the end of the function block). In other words, you could write an iterator that looks like this:

IEnumerable<int> GetOneTwoThree() {
  yield return 1;
  yield return 2;
  yield return 3;
  // We could put "yield break;" here but there's no need, the end of the function signals the same thing.

When you call GetOneTwoThree() it will return an IEnumerable<int>. As mentioned above, there are a few things you can do with this:

var numbers = GetOneTwoThree();
foreach (var number in numbers) {
  Console.WriteLine(number);
  // Output:
var doubledNumbers = numbers.Select(num => num * 2);
foreach (var number in doubledNumbers) {
  Console.WriteLine(number);
  // Output:
var numberArray = numbers.ToArray();
Console.WriteLine(numberArray[0]); // Output: 1

You may notice that we’re iterating over numbers multiple times. For a simple iterator like the one I’ve written, that’s technically okay to do. Every time we iterate over numbers, it will start over at the beginning of the iterator method and yield all the same values over again. (You can’t expect this from every iterator; more on that later.)

Lazy evaluation (the opposite of eager evaluation ) is when we wait to execute a piece of code until we absolutely, positively have to. When you call GetOneTwoThree(), you’ll get a return value despite the fact that none of the code in the function has actually been executed yet! To prove it, run the following code in LINQPad.

(If you’re not familiar with LINQPad , you should check it out—it’s a powerful, portable C# playground. But the only thing you need to know about it here is that it provides the magic .Dump() method, which outputs any value to the Results pane.)

bool didTheCodeRun = false;
void Main() {
  var results = RunTheCode();
  didTheCodeRun.Dump();
IEnumerable<bool> RunTheCode() {
  didTheCodeRun = true;
  yield return true;

The output of running Main() in the above snippet is false. That’s right, after we run RunTheCode(), which explicitly sets didTheCodeRun to true, the value of didTheCodeRun is still false. None of the code in our iterator runs until we start iterating through the IEnumerable. It’s lazily evaluated!

This may seem counterintuitive, but in a lot of cases it’s a good thing. What if you never end up iterating through the IEnumerable at all? Well, that’s a bunch of code the computer didn’t have to execute. Or what if you’re using a LINQ method like .First() to try to find a specific item in the collection? You may not need to run all the code in the iterator to get the value you’re looking for–and you won’t. Once .First() finds a value that matches the predicate, it will stop iterating. If you’re working with an IEnumerable that potentially has thousands of values (or more), you can save a lot of CPU cycles by only iterating as far as you need to.

Of course, it’s all up to you. You can iterate as much or as little as you want. Let’s take a look at some of the ways to do that.

// Here's a variable to track execution of code in an iterator
int lastYielded = -1;
// Here's an iterator for us to play with
IEnumerable<int> GetOneToTen() {
  for (var num = 1; num <= 10; num++) {
    lastYielded = num;
    yield return num;
void Main() {
  var numbers = GetOneToTen();
  lastYielded.Dump(); // Output: -1
  // This gives us an 'instance' of the iteration
  var enumerator = numbers.GetEnumerator();
  // This executes the iterator until the first yield return is reached
  enumerator.MoveNext();
  // This gives us the current (most recently yielded) value of the iterator
  enumerator.Current.Dump(); // Output: 1
  lastYielded.Dump(); // Output: 1
  // This will iterate from 1 to 4, then stop
  foreach (var num in numbers) {
    if (num >= 4) {
      break;
  lastYielded.Dump(); // Output: 4
  // This will not execute any code in the iterator.
  //  LINQ methods are lazily evaluated as well
  var numbersTimesTwo = numbers.Select(num => num * 2);
  lastYielded.Dump(); // Output: 4
  // This will force the entire iterator to run, yielding all values