简体   繁体   中英

Need help understanding C# yield in IEnumerable

i am reading C# 2010 Accelerated. i dont get what is yield

When GetEnumerator is called, the code in the method that contains the yield statement is not actually executed at that point in time. Instead, the compiler generates an enumerator class, and that class contains the yield block code

public IEnumerator<T> GetEnumerator() { 
    foreach( T item in items ) { 
        yield return item; 
    } 
} 

i also read from Some help understanding “yield”

yield is a lazy producer of data, only producing another item after the first has been retrieved, whereas returning a list will return everything in one go.

does this mean that each call to GetEnumerator will get 1 item from the collection? so 1st call i get 1st item, 2nd, i get the 2nd and so on ... ?

Best way to think of it is when you first request an item from an IEnumerator (for example in a foreach ), it starts running trough the method, and when it hits a yield return it pauses execution and returns that item for you to use in your foreach . Then you request the next item, it resumes the code where it left and repeats the cycle until it encounters either yield break or the end of the method.

public IEnumerator<string> enumerateSomeStrings()
{
    yield return "one";
    yield return "two";
    var array = new[] { "three", "four" }
    foreach (var item in array)
        yield return item;
    yield return "five";
}

Take a look at the IEnumerator<T> interface; that may well to clarify what's happening. The compiler takes your code and turns it into a class that implements both IEnumerable<T> and IEnumerator<T> . The call to GetEnumerator() simply returns the class itself.

The implementation is basically a state machine, which, for each call to MoveNext(), executes the code up until the next yield return and then sets Current to the return value. The foreach loop uses this enumerator to walk through the enumerated items, calling MoveNext() before each iteration of the loop. The compiler is really doing some very cool things here, making yield return one of the most powerful constructs in the language. From the programmer's perspective, it's just an easy way to lazily return items upon request.

Yes thats right, heres the example from MSDN that illustrates how to use it

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);
        }
    }
}
/*
Output:
2 4 8 16 32 64 128 256 
*/

If I understand your question correct then your understanding is incorrect I'm affraid. The yield statements (yield return and yield break) is a very clever compiler trick. The code in you method is actually compiled into a class that implements IEnumerable. An instance of this class is what the method will return. Let's Call the instance 'ins' when calling ins.GetEnumerator() you get an IEnumerator that for each Call to MoveNext() produced the next element in the collection (the yield return is responsible for this part) when the sequence has no more elements (eg a yield break is encountered) MoveNext() returns false and further calls results in an exception. So it is not the Call to GetEnumerator that produced the (next) element but the Call to MoveNext

The Simple way to understand yield keyword is we do not need extra class to hold the result of iteration when return using yield return keyword. Generally when we iterate through the collection and want to return the result, we use collection object to hold the result. Let's look at example.

public static List Multiplication(int number, int times)

    {
        List<int> resultList = new List<int>();
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            resultList.Add(result);
        }
        return resultList;
    }

static void Main(string[] args)

    {
        foreach(int i in Multiplication(2,10))
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }

In the above example, I want to return the result of multiplication of 2 ten times. So I Create a method Multiplication which returns me the multiplication of 2 ten times and i store the result in the list and when my main method calls the multiplication method, the control iterates through the loop ten times and store result result in the list. This is without using yield return. Suppose if i want to do this using yield return it looks like

public static IEnumerable Multiplication(int number, int times)

          {
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            yield return result;
        }
    }

static void Main(string[] args)

    {
        foreach(int i in Multiplication(2,10))
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }

Now there is slight changes in Multiplication method, return type is IEnumerable and there is no other list to hold the result because to work with Yield return type must be IEnumerable or IEnumerator and since Yield provides stateful iteration we do not need extra class to hold the result. So in the above example, when Multiplication method is called from Main method, it calculates the result in for 1st iteration and return the result to main method and come backs to the loop and calculate the result for 2nd iteration and returns the result to main method.In this way Yield returns result to calling method one by one in each iteration.There is other Keyword break used in combination with Yield that causes the iteration to stop. For example in the above example if i want to calculate multiplication for only half number of times(10/2=5) then the method looks like this:

public static IEnumerable Multiplication(int number, int times)

    {
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            yield return result;
            if (i == times / 2)
                yield break;
        }

    }

This method now will result multiplication of 2, 5 times.Hope this will help you understand the concept of Yield. For more information please visit http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

It looks like you understand it.

yield is used in your class's GetEnumerator as you describe so that you can write code like this:

foreach (MyObject myObject in myObjectCollection)
{
    // Do something with myObject
}

By returning the first item from the 1st call the second from the 2nd and so on you can loop over all elements in the collection.

yield is defined in MyObjectCollection .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM