yield keyword and lazy evaluation in C#

yield is a new contextual keyword introduced to C# 2.0, vital for lazy evaluation and the performance of queries in LINQ. Being a contextual keyword means that yield can be used as a variable name in C# without any problem. When put before return it becomes a keyword.

yield allows one enumerable class to be implemented in terms of another. This enables the delay of execution of queries until the latest possible moment, skipping the generation of intermediate results that would drastically reduce poerformance. The query operators in LINQ operate on sequence. The result of a query is often another sequence. Lazy evaluation means that until you iterate over the result of the query, the source of the query is not iterated.

To show you how yield works, let’s consider the same class I used in my last post, Winner.

public class Winner
{
    string _name;
    string _country;
    int _year;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
    public string Country
    {
        get { return _country; }
        set { _country = value; }
    }
    public int Year
    {
        get { return _year; }
        set { _year = value; }
    }
    public Winner(string name, string country, int year)
    {
       _name = name;
       _country = country;
       _year = year;
    }
}

and create a class WinnerDB, that contains a list of UEFA Champion League winners. This class implements IEnumerable and returns an enumerator, WinnerEnumerator, to be able to iterate over the winners.

    public class WinnerEnumerator : IEnumerator
    {
        int pos = -1;
        private Winner[] _winners;
        public WinnerEnumerator(Winner[] winners)
        {
            _winners = winners;
        }

        public void Reset()
        {
            pos = -1;
        }

        public bool MoveNext()
        {
            pos++;
            return (pos < _winners.Length);
        }

        public object Current
        {
            get
            {
                try
                {
                    return _winners[pos];

                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }

    public class WinnersDB : IEnumerable
    {
        private Winner[] _winners;
        public WinnersDB()
        {
            _winners = new Winner[]
            {
                new Winner("Barcelona", "Spain", 2006),
                new Winner("Liverpool", "England", 2005),
                new Winner("FC Porto", "Portugal", 2004),
                new Winner("AC Milan", "Italy", 2003),
                new Winner("Real Madrid", "Spain", 2002),
                new Winner("Bayern Munchen", "Germany", 2001),
                new Winner("Real Madrid", "Spain", 2000),
                new Winner("Manchester Utd.", "England", 1999),
                new Winner("Real Madrid", "Spain", 1998),
                new Winner("Olimpique Marseille", "France", 1993),
            };
        }

        public IEnumerator GetEnumerator()
        {
            return new WinnerEnumerator(_winners);
        }
    }

The usage of this class would look like this:

    class Program
    {
        static void Main(string[] args)
        {
            WinnersDB db = new WinnersDB();
            foreach (Winner w in db)
            {
                Console.WriteLine("{0}\t{1}, {2}",
                    w.Year, w.Name, w.Country);
            }
        }
    }

and the output of the program

2006    Barcelona, Spain
2005    Liverpool, England
2004    FC Porto, Portugal
2003    AC Milan, Italy
2002    Real Madrid, Spain
2001    Bayern Munchen, Germany
2000    Real Madrid, Spain
1999    Manchester Utd., England
1998    Real Madrid, Spain
1993    Olimpique Marseille, France

So far so good. But with yield, you can let the compiler do all that stuff for you. When you use yield, the compiler generates an enumerator that keeps the current state of the iteration.

class Program
{
    public static IEnumerable<Winner> WinnersDB()
    {
        Winner [] winners = new Winner[]
        {
                new Winner("Barcelona", "Spain", 2006),
                new Winner("Liverpool", "England", 2005),
                new Winner("FC Porto", "Portugal", 2004),
                new Winner("AC Milan", "Italy", 2003),
                new Winner("Real Madrid", "Spain", 2002),
                new Winner("Bayern Munchen", "Germany", 2001),
                new Winner("Real Madrid", "Spain", 2000),
                new Winner("Manchester Utd.", "England", 1999),
                new Winner("Real Madrid", "Spain", 1998),
                new Winner("Olimpique Marseille", "France", 1993),
        };

        foreach (Winner w in winners)
        {
            yield return w;
        }
    }

    static void Main(string[] args)
    {
        foreach (Winner w in WinnersDB())
        {
            Console.WriteLine("{0}\t{1}, {2}",
                w.Year, w.Name, w.Country);
        }
    }
}

Running this code will produce the same output as the previous one, except that the implementation is much simpler. Perhaps the example is not the best, but should give you a hint of the use of the yield keyword. To see that the source is actually iterated only when the result is iterated, we can modify the WinnersDB method to print a message in the console:

foreach (Winner w in winners)
{
    Console.WriteLine("yield: {0} {1}, {2}", w.Year, w.Name, w.Country);
    yield return w;
}

In this case, the output looks like this:

yield: 2006 Barcelona, Spain
2006    Barcelona, Spain
yield: 2005 Liverpool, England
2005    Liverpool, England
yield: 2004 FC Porto, Portugal
2004    FC Porto, Portugal
yield: 2003 AC Milan, Italy
2003    AC Milan, Italy
yield: 2002 Real Madrid, Spain
2002    Real Madrid, Spain
yield: 2001 Bayern Munchen, Germany
2001    Bayern Munchen, Germany
yield: 2000 Real Madrid, Spain
2000    Real Madrid, Spain
yield: 1999 Manchester Utd., England
1999    Manchester Utd., England
yield: 1998 Real Madrid, Spain
1998    Real Madrid, Spain
yield: 1993 Olimpique Marseille, France
1993    Olimpique Marseille, France

1 Reply to “yield keyword and lazy evaluation in C#”

  1. Actually, you do not need to build that array up front and the foreach in the sample, you can just yield return for each new Winner and be even more lazy.

    e.g.

    public static IEnumerable WinnerDB()
    {
    yield return new Winner(“Barcelona”, “Spain”, 2006);
    yield return new Winner(“FC Porto”, “Portugal”, 2004);

    }

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.