173

Given this code:

IEnumerable<object> FilteredList()
{
    foreach( object item in FullList )
    {
        if( IsItemInPartialList( item ) )
            yield return item;
    }
}

Why should I not just code it this way?:

IEnumerable<object> FilteredList()
{
    var list = new List<object>(); 
    foreach( object item in FullList )
    {
        if( IsItemInPartialList( item ) )
            list.Add(item);
    }
    return list;
}

I sort of understand what the yield keyword does. It tells the compiler to build a certain kind of thing (an iterator). But why use it? Apart from it being slightly less code, what's it do for me?

Marcel Gosselin
  • 4,610
  • 2
  • 31
  • 54
James P. Wright
  • 8,991
  • 23
  • 79
  • 142

8 Answers8

245

Using yield makes the collection lazy.

Let's say you just need the first five items. Your way, I have to loop through the entire list to get the first five items. With yield, I only loop through the first five items.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • 15
    Note that using `FullList.Where(IsItemInPartialList)` will be just as lazy. Only, it requires far less compiler generated custom ---gunk--- code. And less developer time writing and maintaining. (Of course, that was just this example) – sehe Dec 28 '12 at 11:57
  • 5
    That's Linq, isn't it? I imagine Linq does something very similar under the covers. – Robert Harvey Dec 28 '12 at 16:14
  • 1
    Yes, Linq used delayed execution (`yield return`) where ever possible. – Chad Schouggins Jan 02 '13 at 01:36
  • 12
    Don't forget that if the yield return statement never executes, you still get an empty collection result so there's no need to worry about a null reference exception. yield return is awesome with chocolate sprinkles. – Razor Jan 06 '13 at 09:43
129

The benefit of iterator blocks is that they work lazily. So you can write a filtering method like this:

public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
                                   Func<T, bool> predicate)
{
    foreach (var item in source)
    {
        if (predicate(item))
        {
            yield return item;
        }
    }
}

That will allow you to filter a stream as long as you like, never buffering more than a single item at a time. If you only need the first value from the returned sequence, for example, why would you want to copy everything into a new list?

As another example, you can easily create an infinite stream using iterator blocks. For example, here's a sequence of random numbers:

public static IEnumerable<int> RandomSequence(int minInclusive, int maxExclusive)
{
    Random rng = new Random();
    while (true)
    {
        yield return rng.Next(minInclusive, maxExclusive);
    }
}

How would you store an infinite sequence in a list?

My Edulinq blog series gives a sample implementation of LINQ to Objects which makes heavy use of iterator blocks. LINQ is fundamentally lazy where it can be - and putting things in a list simply doesn't work that way.

Martin Smith
  • 438,706
  • 87
  • 741
  • 845
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I'm not sure whether to like your `RandomSequence` or not. To me, IEnumerable means -first and foremost- that I can iterate with foreach, but this would obviously lead to an inifinite loop here. I'd consider this a pretty dangerous misuse of the IEnumerable concept, but YMMV. – Sebastian Negraszus Jan 17 '13 at 17:10
  • 6
    @SebastianNegraszus: A sequence of random numbers is logically infinite. You could easily create an `IEnumerable` representing the Fibonacci sequence, for example. You can use `foreach` with it, but nothing about `IEnumerable` guarantees that it will be finite. – Jon Skeet Jan 17 '13 at 17:21
42

With the "list" code, you have to process the full list before you can pass it on to the next step. The "yield" version passes the processed item immediately to the next step. If that "next step" contains a ".Take(10)" then the "yield" version will only process the first 10 items and forget about the rest. The "list" code would have processed everything.

This means that you see the most difference when you need to do a lot of processing and/or have long lists of items to process.

Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
23

You can use yield to return items that aren't in a list. Here's a little sample that could iterate infinitely through a list until canceled.

public IEnumerable<int> GetNextNumber()
{
    while (true)
    {
        for (int i = 0; i < 10; i++)
        {
            yield return i;
        }
    }
}

public bool Canceled { get; set; }

public void StartCounting()
{
    foreach (var number in GetNextNumber())
    {
        if (this.Canceled) break;
        Console.WriteLine(number);
    }
}

This writes

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4

...etc. to the console until canceled.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jason Whitted
  • 4,059
  • 1
  • 16
  • 16
10
object jamesItem = null;
foreach(var item in FilteredList())
{
   if (item.Name == "James")
   {
       jamesItem = item;
       break;
   }
}
return jamesItem;

When the above code is used to loop through FilteredList() and assuming item.Name == "James" will be satisfied on 2nd item in the list, the method using yield will yield twice. This is a lazy behavior.

Where as the method using list will add all the n objects to the list and pass the complete list to the calling method.

This is exactly a use case where difference between IEnumerable and IList can be highlighted.

zero323
  • 322,348
  • 103
  • 959
  • 935
humblelistener
  • 1,456
  • 12
  • 22
8

The best real world example I've seen for the use of yield would be to calculate a Fibonacci sequence.

Consider the following code:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(string.Join(", ", Fibonacci().Take(10)));
        Console.WriteLine(string.Join(", ", Fibonacci().Skip(15).Take(1)));
        Console.WriteLine(string.Join(", ", Fibonacci().Skip(10).Take(5)));
        Console.WriteLine(string.Join(", ", Fibonacci().Skip(100).Take(1)));
        Console.ReadKey();
    }

    private static IEnumerable<long> Fibonacci()
    {
        long a = 0;
        long b = 1;

        while (true)
        {
            long temp = a;
            a = b;

            yield return a;

            b = temp + b;
        }
    }
}

This will return:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55
987
89, 144, 233, 377, 610
1298777728820984005

This is nice because it allows you to calculate out an infinite series quickly and easily, giving you the ability to use the Linq extensions and query only what you need.

Middas
  • 1,862
  • 1
  • 29
  • 44
1

why use [yield]? Apart from it being slightly less code, what's it do for me?

Sometimes it is useful, sometimes not. If the entire set of data must be examined and returned then there is not going to be any benefit in using yield because all it did was introduce overhead.

When yield really shines is when only a partial set is returned. I think the best example is sorting. Assume you have a list of objects containing a date and a dollar amount from this year and you would like to see the first handful (5) records of the year.

In order to accomplish this, the list must be sorted ascending by date, and then have the first 5 taken. If this was done without yield, the entire list would have to be sorted, right up to making sure the last two dates were in order.

However, with yield, once the first 5 items have been established the sorting stops and the results are available. This can save a large amount of time.

Travis J
  • 81,153
  • 41
  • 202
  • 273
0

The yield return statement allows you to return only one item at a time. You are collecting all the items in a list and again returning that list, which is a memory overhead.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Prabhavith
  • 458
  • 1
  • 4
  • 15