78

After almost 4 years of experience, I haven't seen a code where yield keyword is used. Can somebody show me a practical usage (along explanation) of this keyword, and if so, aren't there other ways easier to fullfill what it can do?

4
  • 9
    All (or at least most) of LINQ is implemented using yield. Also Unity3D framework has found some good use for it - it is used to pause functions (on yield statements) and resume it later using the state in the IEnumerable.
    – Daniel
    Commented Jul 30, 2011 at 18:34
  • 2
    Shouldn't this be moved to StackOverflow? Commented Jul 31, 2011 at 12:29
  • 4
    @Danny - It's not suitable for Stack Overflow, as the question isn't asking to solve a specific problem but asking about what yield can be used for in general.
    – ChrisF
    Commented Jul 31, 2011 at 13:05
  • 10
    For real? I can't think of a single app where I haven't used it.
    – Aaronaught
    Commented Jul 31, 2011 at 15:48

8 Answers 8

114

Efficiency

The yield keyword effectively creates a lazy enumeration over collection items that can be much more efficient. For example, if your foreach loop iterates over just the first 5 items of 1 million items then that's all yield returns, and you didn't build up a collection of 1 million items internally first. Likewise you will want to use yield with IEnumerable<T> return values in your own programming scenarios to achieve the same efficiencies.

Example of efficiency gained in a certain scenario

Not an iterator method, potential inefficient use of a big collection,
(Intermediate collection is built having lots of items)

// Method returns all million items before anything can loop over them. 
List<object> GetAllItems() {
    List<object> millionCustomers;
    database.LoadMillionCustomerRecords(millionCustomers); 
    return millionCustomers;
}

// MAIN example ---------------------
// Caller code sample:
int num = 0;
foreach(var itm in GetAllItems())  {
    num++;
    if (num == 5)
        break;
}
// Note: One million items returned, but only 5 used. 

Iterator version, efficient
(No intermediate collection is built)

// Yields items one at a time as the caller's foreach loop requests them
IEnumerable<object> IterateOverItems() {
    for (int i; i < database.Customers.Count(); ++i)
        yield return database.Customers[i];
}

// MAIN example ---------------------
// Caller code sample:
int num = 0;
foreach(var itm in IterateOverItems())  {
    num++;
    if (num == 5)
        break;
}
// Note: Only 5 items were yielded and used out of the million.

Simplify some programming scenarios

In another case, it makes some kinds of sorting and merging of lists easier to program because you just yield items back in the desired order rather than sorting them into an intermediate collection and swapping them in there. There are many such scenarios.

Just one example is the merging of two lists:

IEnumerable<object> EfficientMerge(List<object> list1, List<object> list2) {
    foreach(var o in list1) 
        yield return o; 
    foreach(var o in list2) 
        yield return o;
}

This method yields back one contiguous list of items, effectively a merge with no intermediate collection needed.

More Info

The yield keyword can only be used in context of an iterator method (having a return type of IEnumerable, IEnumerator, IEnumerable<T>, or IEnumerator<T>.) and there is a special relationship with foreach. Iterators are special methods. The MSDN yield documentation and iterator documentation contains lots of interesting information and explanation of the concepts. Be sure to correlate it with the foreach keyword by reading about it too, to supplement your understanding of iterators.

To learn about how the iterators achieve their efficiency, the secret is in the IL code generated by the C# compiler. The IL generated for an iterator method differs drastically from that generated for a regular (non-iterator) method. This article (What Does the Yield Keyword Really Generate?) provides that kind of insight.

4
  • 2
    They're particularly useful for algorithms that take a (possibly long) sequence and generate another one where the mapping is not one-to-one. An example of this is polygon clipping; any particular edge may generate many or even no edges once clipped. Iterators make this enormously easier to express, and yielding is one of the best ways to write them. Commented Jul 31, 2011 at 11:25
  • 3
    Once upon a time, I used yield to build packets for a binary network protocol. It seemed the most natural choice in C#. Commented Jul 31, 2011 at 16:19
  • 4
    Doesn't the database.Customers.Count() enumerate the entire customers enumeration, thus requiring the more efficient code to go through every item?
    – Stephen
    Commented Jun 9, 2015 at 23:04
  • 5
    Call me anal, but that is concatenation, not merging. (And linq already has a Concat method.)
    – OldFart
    Commented Jun 10, 2015 at 16:58
4

Some time ago I had a practical example, let's assume you have a situation like this:

List<Button> buttons = new List<Button>();
void AddButtons()
{
   for ( int i = 0; i <= 10; i++ ) {
      var button = new Button();
      buttons.Add(button);
      button.Click += (sender, e) => 
          MessageBox.Show(String.Format("You clicked button number {0}", ???));
   }
}

The button object does not know his own position in the collection. The same limitation applies for Dictionary<T> or other collections types.

Here is my solution using yield keyword:

interface IHasId { int Id { get; set; } }

class IndexerList<T>: List<T>, IEnumerable<T> where T: IHasId
{
   List<T> elements = new List<T>();
   new public void Clear() { elements.Clear(); }
   new public void Add(T element) { elements.Add(element); }
   new public int Count { get { return elements.Count; } }    
   new public IEnumerator<T> GetEnumerator()
   {
      foreach ( T c in elements )
         yield return c;
   }

   new public T this[int index]
   {
      get
      {
         foreach ( T c in elements ) {
            if ( (int)c.Id == index )
               return c;
         }
         return default(T);
      }
   }
}

And that's how I use it:

class ButtonWithId: Button, IHasId
{
   public int Id { get; private set; }
   public ButtonWithId(int id) { this.Id = id; }
}

IndexerList<ButtonWithId> buttons = new IndexerList<ButtonWithId>();
void AddButtons()
{
   for ( int i = 10; i <= 20; i++ ) {
      var button = new ButtonWithId(i);
      buttons.Add(button);
      button.Click += (sender, e) => 
         MessageBox.Show(String.Format("You clicked button number {0}", ( (ButtonWithId)sender ).Id));
   }
}

I don't have to make a for loop over my collection in order to find the index. My Button has an ID and this is also used as index in IndexerList<T>, so you avoid any redundant ID's or indexes - that's what I like! The index/Id can be an arbitrary number.

1
  • "I don't have to make a for loop over my collection in order to find the index." but you are essentially doing it inside the indexer now. Commented Jan 20, 2021 at 17:17
2

A practical example may be found here:

http://www.ytechie.com/2009/02/using-c-yield-for-readability-and-performance.html

There are a number of advantages of using yield over standard code:

  • If the iterator is used to build a list then you can yield the return and the caller can decide whether he wants that result in a list, or not.
  • The caller may also decide to cancel the iteration for a reason that is outside of the scope of what you are doing in the iteration.
  • Code is a bit shorter.

However, as Jan_V said (just beat me to it by a few seconds :-) you can live without it because internally the compiler will produce code almost identical in both cases.

1

Here's an example:

https://bitbucket.org/ant512/workingweek/src/a745d02ba16f/source/WorkingWeek/Week.cs#cl-158

The class performs date calculations based on a working week. I can tell an instance of the class that Bob works 9:30 to 17:30 every week day with an hour's break for lunch at 12:30. With this knowledge, the AscendingShifts() function will yield working shift objects between the supplied dates. To list all of Bob's working shifts between Jan 1 and Feb 1 this year, you'd use it like this:

foreach (var shift in week.AscendingShifts(new DateTime(2011, 1, 1), new DateTime(2011, 2, 1)) {
    Console.WriteLine(shift);
}

The class doesn't really iterate over a collection. However, the shifts between two dates can be thought of as a collection. The yield operator makes it possible to iterate over this imagined collection without creating the collection itself.

1

I have a small db data layer that has a command class in which you set the SQL command text, the command type, and return a IEnumerable of 'command parameters'.

Basically the idea is to have typed CLR commands instead of manually filling SqlCommand properties and parameters all the time.

So there is a function that looks like this:

IEnumerable<DbParameter> GetParameters()
{
    // here i do something like

    yield return new DbParameter { name = "@Age", value = this.Age };

    yield return new DbParameter { name = "@Name", value = this.Name };
}

The class that inherits this command class has the properties Age and Name.

Then you can new up a command object filled its properties and pass it to a db interface which actually does the command call.

All in all makes it really easy to work with SQL commands and keep them typed.

1

Although the merging case has already been covered in the accepted answer, let me show you the yield-merge params extension method™:

public static IEnumerable<T> AppendParams<T>(this IEnumerable<T> a, params T[] b)
{
    foreach (var el in a) yield return el;
    foreach (var el in b) yield return el;
}

I use this to build packets of a network protocol:

static byte[] MakeCommandPacket(string cmd)
{
    return
        header
        .AppendParams<byte>(0, 0, 1, 0, 0, 1, 0x92, 0, 0, 0, 0)
        .AppendAscii(cmd)
        .MarkLength()
        .MarkChecksum()
        .ToArray();
}

The MarkChecksum method, for example, looks like this. And it has a yield, too:

public static IEnumerable<byte> MarkChecksum(this IEnumerable<byte> data, int pos = 6)
{
    foreach (byte b in data)
    {
        yield return pos-- == 0 ? (byte)data.Sum(z => z) : b;
    }
}

But be careful when using aggregate methods like Sum() in an enumeration method as they trigger a separate enumeration process.

1

Elastic Search .NET example repo has a great example of using yield return to partition a collection into multiple collections with a specified size:

https://github.com/elastic/elasticsearch-net-example/blob/master/src/NuSearch.Domain/Extensions/PartitionExtension.cs

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int size)
    {
        T[] array = null;
        int count = 0;
        foreach (T item in source)
        {
            if (array == null)
            {
                array = new T[size];
            }
            array[count] = item;
            count++;
            if (count == size)
            {
                yield return new ReadOnlyCollection<T>(array);
                array = null;
                count = 0;
            }
        }
        if (array != null)
        {
            Array.Resize(ref array, count);
            yield return new ReadOnlyCollection<T>(array);
        }
    }
0

Expanding upon Jan_V's answer, I just hit a real-world case related to it:

I needed to use the Kernel32 versions of FindFirstFile/FindNextFile. You get a handle from the first call and feed it to all subsequent calls. Wrap this in a enumerator and you get something you can directly use with foreach.

Not the answer you're looking for? Browse other questions tagged or ask your own question.