I ran into this keyword by accident (yield, accident… very funny), and I must say – this accident was a good one!
yield is a small keyword who has gotten used to live by the shadows of the celebrity keywords (static, const, Paris Hilton, Madonna, etc.), and I say – let's change it! Let's give yield what it deserves!
Why am I so thrilled about it?
OK, so let's introduce yield first – yield comes to help us to create an enumerator out of a single method.
For example, this code:
public static IEnumerable YieldTest(int num)
{
for (int i = 0; i < num; i++)
{
yield return i;
}
yield break;
}
static void Main(string[] args)
{
foreach (int num in YieldTest(8))
{
Console.WriteLine(num);
}
}
Will result in:
0 1 2 3 4 5 6 7 Press any key to continue . . . |
As you could see, yield has 2 possible continuations – return and break:
yield return <something> means that <something> is going to be the next enumerator value.
yield break means that the party is over and so is our enumeration…
This means that yield return does not act as a regular return keyword – it doesn't really end the method's execution. yield return pauses the method execution and the next time you call it (for the next enumeration value), the method will continue to execute from the last yield return call. It sounds a bit confusing I think… Think of it that way – IEnumerator.MoveNext() really moves forward in the method till the next yield return\break statement.
Still asking yourself why am I so thrilled about my new precious keyword?
Well, this is a code saver! and I like code savers!
I can see it saves me code lines on 2 occasions:
1. When developing my very own enumerable object. See the difference in the next 2 samples:
A regular implementation (2 classes, 44 lines):
public class Users :IEnumerable { // — IEnumerable Members —
public IEnumerator GetEnumerator() { return new UserEnumerator(10); } }
public class UserEnumerator : IEnumerator { private int m_index; private int m_maxIndex;
private string GetUserName(int index) { return "User #" + index.ToString(); }
public UserEnumerator(int maxIndex) { m_maxIndex = maxIndex; Reset(); }
// — IEnumerator Members —
public object Current { get { return GetUserName(m_index); } }
public bool MoveNext() { m_index++; return (m_index < m_maxIndex); }
public void Reset() { m_index = -1; } } |
A yield implementation (1 class, 19 lines):
public class UsersYield :IEnumerable { private string GetUserName(int index) { return "User #" + index.ToString(); }
// — IEnumerable Members —
public IEnumerator GetEnumerator() { for (int i = 0; i < 10; i++) { yield return GetUserName(i); }
yield break; } } |
2. When developing a method that returns a list and I know that all I'm gonna do is loop over it. Here, the amount of code it saves is not as big as the in the previous bullet, but the code makes more sense, which is even better! For example:
private IEnumerable GetDaysOfWeek() { yield return "Sunday"; yield return "Monday"; yield return "Tuesday"; yield return "Wednesday"; yield return "Thursday"; yield return "Friday"; yield return "Saturday"; yield break; }
public void LoopOverDaysOfWeek() { foreach (string day in GetDaysOfWeek()) { // do something } } |
Did you notice in the last 2 sample codes that yield can return IEnumerator and IEnumerable? The fun just never stops!
So the yield keyword is indeed very helpful BUT, and it's a big but as you can see, be careful with it!
For instance, one can go and write the following code:
public IEnumerable BadYieldUsage() { int i = 100; while (i > 0) { if (i % 2 == 0) { if (i > 12) { yield return "Even"; } else { yield break; } } else { yield return "Odd"; } i–; } } |
I want to promise you – if you write a similar code block and actually use it, the end of the world, without any doubt, will come.
Keep the enumeration code as simple as you can because debugging it afterwards will turn you into a suicidal human being.
In conclusion, the yield keyword on the one hand, will save you lines of code, make it more readable in some cases and eventually make you happier. On the other hand, overusing it might kill you.
Have fun!
Shay.
Share this post : | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
In the simplest of cases (which is typically where I'm using yield), you don't even need the yield break at the end. When the method returns, it's assumed that the enumeration is complete. I used the yield keyword for awhile before I even realized that there was a yield break 😛
There's a way to avoid surprises like this… reading the language spec isn't as boring as you might imagine.
BTW, debugging the yield keyword isn't too hard in my experience.
Great posting!!
I explained how the yield statement works behind the scenes of the C# compiler.
http://startbigthinksmall.wordpress.com/2008/06/09/behind-the-scenes-of-the-c-yield-keyword/
what is wrong with BadYieldUsage method? I tried it and does the job.
@alphatester – the BadYieldUsage method works great, I only gave it as an example of implementations you should avoid because it will be pretty annoying to debug.
Man, People funny like you explains it better! 🙂
Thanks for sharing!
This is very nice article.
Article not only tells the basic use of yield but also tells the precausions to be takes.
This will be very helpful.
Thank you for publishing such an article.
Looking forward to see yours many such articles.
Still don't understand why I would ever want to use yield. Been to several sites now trying to understand what's so great about this and all I get are examples with no explanation as to what benefits this adds. What was wrong with the BadYieldUsage method?? No reason was ever given.