I have seen the yield keyword being used quite a lot on Stack Overflow and blogs. I don't use LINQ. Can someone explain the yield keyword?
I know that similar questions exist. But none really explain what is its use in plain simple language.
I have seen the yield keyword being used quite a lot on Stack Overflow and blogs. I don't use LINQ. Can someone explain the yield keyword?
I know that similar questions exist. But none really explain what is its use in plain simple language.
By far the best explanation of this (that I've seen) is Jon Skeet's book - and that chapter is free! Chapter 6, C# in Depth. There is nothing I can add here that isn't covered.
Then buy the book; you will be a better C# programmer for it.
Q: Why didn't I write a longer answer here (paraphrased from comments); simple. As Eric Lippert observes (here), the yield
construct (and the magic that goes behind it) is the single most complex bit of code in the C# compiler, and to try and describe it in a brief reply here is naïve at best. There are so many nuances to yield
that IMO it is better to refer to a pre-existing (and fully qualified) resource.
Eric's blog now has 7 entries (and that is just the recent ones) discussing yield
. I have a vast amount of respect for Eric, but his blog is probably more appropriate as a "further information" for people who are comfortable with the subject (yield
in this case), as it typically describes a lot of the background design considerations. Best done in the context of a reasonable foundation.
(and yes, chapter 6 does download; I verified...)
The yield
keyword is used with methods that return IEnumerable<T>
or IEnumerator<T>
and it makes the compiler generate a class that implements the necessary plumbing for using the iterator. E.g.
public IEnumerator<int> SequenceOfOneToThree() {
yield return 1;
yield return 2;
yield return 3;
}
Given the above the compiler will generate a class that implements IEnumerator<int>
, IEnumerable<int>
and IDisposable
(actually it will also implement the non-generic versions of IEnumerable
and IEnumerator
).
This allows you to call the method SequenceOfOneToThree
in a foreach
loop like this
foreach(var number in SequenceOfOneToThree) {
Console.WriteLine(number);
}
An iterator is a state machine, so each time yield
is called the position in the method is recorded. If the iterator is moved to the next element, the method resumes right after this position. So the first iteration returns 1 and marks that position. The next iterator resumes right after one and thus returns 2 and so forth.
Needless to say you can generate the sequence in any way you like, so you don't have to hard code the numbers like I did. Also, if you want to break the loop you can use yield break
.
In an effort to demystify I'll avoid talking about iterators, since they could be part of the mystery themselves.
the yield return and yield break statements are most often used to provide "deferred evaluation" of the collection.
What this means is that when you get the value of a method that uses yield return, the collection of things you are trying to get don't exist together yet (it's essentially empty). As you loop through them (using foreach) it will execute the method at that time and get the next element in the enumeration.
Certain properties and methods will cause the entire enumeration to be evaluated at once (such as "Count").
Here's a quick example of the difference between returning a collection and returning yield:
string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };
public IEnumerable<string> GetYieldEnumerable()
{
foreach (var name in names)
yield return name;
}
public IEnumerable<string> GetList()
{
var list = new List<string>();
foreach (var name in names)
list.Add(name);
return list;
}
// we're going to execute the GetYieldEnumerable() method
// but the foreach statement inside it isn't going to execute
var yieldNames = GetNamesEnumerable();
// now we're going to execute the GetList() method and
// the foreach method will execute
var listNames = GetList();
// now we want to look for a specific name in yieldNames.
// only the first two iterations of the foreach loop in the
// GetYieldEnumeration() method will need to be called to find it.
if (yieldNames.Contains("Jim")
Console.WriteLine("Found Jim and only had to loop twice!");
// now we'll look for a specific name in listNames.
// the entire names collection was already iterated over
// so we've already paid the initial cost of looping through that collection.
// now we're going to have to add two more loops to find it in the listNames
// collection.
if (listNames.Contains("Jim"))
Console.WriteLine("Found Jim and had to loop 7 times! (5 for names and 2 for listNames)");
This can also be used if you need to get a reference to the Enumeration before the source data has values. For example if the names collection wasn't complete to start with:
string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };
public IEnumerable<string> GetYieldEnumerable()
{
foreach (var name in names)
yield return name;
}
public IEnumerable<string> GetList()
{
var list = new List<string>();
foreach (var name in names)
list.Add(name);
return list;
}
var yieldNames = GetNamesEnumerable();
var listNames = GetList();
// now we'll change the source data by renaming "Jim" to "Jimbo"
names[1] = "Jimbo";
if (yieldNames.Contains("Jimbo")
Console.WriteLine("Found Jimbo!");
// Because this enumeration was evaluated completely before we changed "Jim"
// to "Jimbo" it isn't going to be found
if (listNames.Contains("Jimbo"))
// this can't be true
else
Console.WriteLine("Couldn't find Jimbo, because he wasn't there when I was evaluated.");
The yield
keyword is a convenient way to write an IEnumerator
. For example:
public static IEnumerator<int> Range(int from, int to)
{
for (int i = from; i < to; i++)
{
yield return i;
}
}
is transformed by the C# compiler to something similiar to:
public static IEnumerator<int> Range(int from, int to)
{
return new RangeEnumerator(from, to);
}
class RangeEnumerator : IEnumerator<int>
{
private int from, to, current;
public RangeEnumerator(int from, int to)
{
this.from = from;
this.to = to;
this.current = from;
}
public bool MoveNext()
{
this.current++;
return this.current < this.to;
}
public int Current
{
get
{
return this.current;
}
}
}
Take a look at the MSDN documentation and the example. It is essentially an easy way to create an iterator in C#.
public class List
{
//using System.Collections;
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while (counter++ < exponent)
{
result = result * number;
yield return result;
}
}
static void Main()
{
// Display powers of 2 up to the exponent 8:
foreach (int i in Power(2, 8))
{
Console.Write("{0} ", i);
}
}
}
Eric White's series on functional programming it well worth the read in it's entirety, but the entry on Yield is as clear an explanation as I've seen.
yield
is not directly related to LINQ, but rather to iterator blocks. The linked MSDN article gives great detail on this language feature. See especially the Using Iterators section. For deep details of iterator blocks, see Eric Lippert's recent blog posts on the feature. For the general concept, see the Wikipedia article on iterators.
I came up with this to overcome a .NET shortcoming having to manually deep copy List.
I use this:
static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
foreach (SpotPlacement sp in spotPlacements)
{
yield return (SpotPlacement)sp.Clone();
}
}
And at another place:
public object Clone()
{
OrderItem newOrderItem = new OrderItem();
...
newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
...
return newOrderItem;
}
I tried to come up with oneliner that does this, but it's not possible, due to yield not working inside anonymous method blocks.
EDIT:
Better still, use a generic List cloner:
class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}
Let me add to all of this. Yield is not a keyword. It will only work if you use "yield return" other than that it will work like a normal variable.
It's uses to return iterator from a function. You can search further on that. I recommend searching for "Returning Array vs Iterator"