Search code examples
c#yield

What is the yield keyword used for in C#?


In the How Can I Expose Only a Fragment of IList<> question one of the answers had the following code snippet:

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}

What does the yield keyword do there? I've seen it referenced in a couple places, and one other question, but I haven't quite figured out what it actually does. I'm used to thinking of yield in the sense of one thread yielding to another, but that doesn't seem relevant here.


Solution

  • The yield contextual keyword actually does quite a lot here.

    The function returns an object that implements the IEnumerable<object> interface. If a calling function starts foreaching over this object, the function is called again until it "yields". This is syntactic sugar introduced in C# 2.0. In earlier versions you had to create your own IEnumerable and IEnumerator objects to do stuff like this.

    The easiest way understand code like this is to type-in an example, set some breakpoints and see what happens. Try stepping through this example:

    public void Consumer()
    {
        foreach(int i in Integers())
        {
            Console.WriteLine(i.ToString());
        }
    }
    
    public IEnumerable<int> Integers()
    {
        yield return 1;
        yield return 2;
        yield return 4;
        yield return 8;
        yield return 16;
        yield return 16777216;
    }
    

    When you step through the example, you'll find the first call to Integers() returns 1. The second call returns 2 and the line yield return 1 is not executed again.

    Here is a real-life example:

    public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
    {
        using (var connection = CreateConnection())
        {
            using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
            {
                command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        yield return make(reader);
                    }
                }
            }
        }
    }