Give way to the yield keyword!

6 ביוני 2008

8 תגובות

Yield! 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.

Fun fun fun!

Have fun!
Shay.


Share this post :

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

8 תגובות

  1. Mark Hildreth7 ביוני 2008 ב 21:10

    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 :P

    להגיב
  2. James Lanng8 ביוני 2008 ב 20:48

    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.

    להגיב
  3. Lars Corneliussen10 ביוני 2008 ב 0:38

    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/

    להגיב
  4. alphatester7 בינואר 2009 ב 0:30

    what is wrong with BadYieldUsage method? I tried it and does the job.

    להגיב
  5. shayf7 בינואר 2009 ב 9:38

    @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.

    להגיב
  6. The Guy Who Laughed!25 בספטמבר 2009 ב 15:56

    Man, People funny like you explains it better! :)

    Thanks for sharing!

    להגיב
  7. Tejendra Garge6 באפריל 2010 ב 15:15

    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.

    להגיב
  8. Matt20 בינואר 2012 ב 1:17

    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.

    להגיב