Question

In my everlasting quest to suck less I'm trying to understand the "yield" statement, but I keep encountering the same error.

The body of [someMethod] cannot be an iterator block because 'System.Collections.Generic.List< AClass>' is not an iterator interface type.

This is the code where I got stuck:

foreach (XElement header in headersXml.Root.Elements()){
    yield return (ParseHeader(header));                
}

What am I doing wrong? Can't I use yield in an iterator? Then what's the point? In this example it said that List<ProductMixHeader> is not an iterator interface type. ProductMixHeader is a custom class, but I imagine List is an iterator interface type, no?

--Edit--
Thanks for all the quick answers.
I know this question isn't all that new and the same resources keep popping up.
It turned out I was thinking I could return List<AClass> as a return type, but since List<T> isn't lazy, it cannot. Changing my return type to IEnumerable<T> solved the problem :D

A somewhat related question (not worth opening a new thread): is it worth giving IEnumerable<T> as a return type if I'm sure that 99% of the cases I'm going to go .ToList() anyway? What will the performance implications be?

Was it helpful?

Solution

A method using yield return must be declared as returning one of the following two interfaces:

IEnumerable<SomethingAppropriate>
IEnumerator<SomethingApropriate>

(thanks Jon and Marc for pointing out IEnumerator)

Example:

public IEnumerable<AClass> YourMethod()
{
    foreach (XElement header in headersXml.Root.Elements())
    {
        yield return (ParseHeader(header));                
    }
}

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.

So there is a difference, and you need to declare the method correctly.

For more information, read Jon's answer here, which contains some very useful links.

OTHER TIPS

It's a tricky topic. In a nutshell, it's an easy way of implementing IEnumerable and its friends. The compiler builds you a state machine, transforming parameters and local variables into instance variables in a new class. Complicated stuff.

I have a few resources on this:

"yield" creates an iterator block - a compiler generated class that can implement either IEnumerable[<T>] or IEnumerator[<T>]. Jon Skeet has a very good (and free) discussion of this in chapter 6 of C# in Depth.

But basically - to use "yield" your method must return an IEnumerable[<T>] or IEnumerator[<T>]. In this case:

public IEnumerable<AClass> SomeMethod() {
    // ...
    foreach (XElement header in headersXml.Root.Elements()){
        yield return (ParseHeader(header));                
    }
}

List implements Ienumerable.

Here's an example that might shed some light on what you are trying to learn. I wrote this about 6 months

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YieldReturnTest
{
    public class PrimeFinder
    {
        private Boolean isPrime(int integer)
        {
            if (0 == integer)
                return false;

            if (3 > integer)
                return true;

            for (int i = 2; i < integer; i++)
            {
                if (0 == integer % i)
                    return false;
            }
            return true;
        }

        public IEnumerable<int> FindPrimes()
        {
            int i;

            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    yield return i;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PrimeFinder primes = new PrimeFinder();

            foreach (int i in primes.FindPrimes())
            {
                Console.WriteLine(i);
                Console.ReadLine();
            }

            Console.ReadLine();
            Console.ReadLine();
        }
    }
}

I highly recommend using Reflector to have a look at what yield actually does for you. You'll be able to see the full code of the class that the compiler generates for you when using yield, and I've found that people understand the concept much more quickly when they can see the low-level result (well, mid-level I guess).

To understand yield, you need to understand when to use IEnumerator and IEnumerable (because you have to use either of them). The following examples help you to understand the difference.

First, take a look at the following class, it implements two methods - one returning IEnumerator<int>, one returning IEnumerable<int>. I'll show you that there is a big difference in usage, although the code of the 2 methods is looking similar:

// 2 iterators, one as IEnumerator, one as IEnumerable
public class Iterator
{
    public static IEnumerator<int> IterateOne(Func<int, bool> condition)
    {
        for(var i=1; condition(i); i++) { yield return i; }     
    }
    public static IEnumerable<int> IterateAll(Func<int, bool> condition)
    {
        for(var i=1; condition(i); i++) { yield return i; }     
    }
}

Now, if you're using IterateOne you can do the following:

    // 1. Using IEnumerator allows to get item by item
    var i=Iterator.IterateOne(x => true); // iterate endless
    // 1.a) get item by item
    i.MoveNext(); Console.WriteLine(i.Current);
    i.MoveNext(); Console.WriteLine(i.Current);
    // 1.b) loop until 100
    int j; while (i.MoveNext() && (j=i.Current)<=100) { Console.WriteLine(j); }

1.a) prints:

1
2

1.b) prints:

3
4
...
100

because it continues counting right after the 1.a) statements have been executed.

You can see that you can advance item by item using MoveNext().


In contrast, IterateAll allows you to use foreach and also LINQ statements for bigger comfort:

    // 2. Using IEnumerable makes looping and LINQ easier   
    var k=Iterator.IterateAll(x => x<100); // limit iterator to 100
    // 2.a) Use a foreach loop
    foreach(var x in k){ Console.WriteLine(x); } // loop
    // 2.b) LINQ: take 101..200 of endless iteration
    var lst=Iterator.IterateAll(x=>true).Skip(100).Take(100).ToList(); // LINQ: take items
    foreach(var x in lst){ Console.WriteLine(x); } // output list

2.a) prints:

1
2
...
99

2.b) prints:

101
102
...
200


Note: Since IEnumerator<T> and IEnumerable<T> are Generics, they can be used with any type. However, for simplicity I have used int in my examples for type T.

This means, you can use one of the return types IEnumerator<ProductMixHeader> or IEnumerable<ProductMixHeader> (the custom class you have mentioned in your question).

The type List<ProductMixHeader> does not implement any of these interfaces, which is the reason why you can't use it that way. But Example 2.b) is showing how you can create a list from it.

If you're creating a list by appending .ToList() then the implication is, that it will create a list of all elements in memory, while an IEnumerable allows lazy creation of its elements - in terms of performance, it means that elements are enumerated just in time - as late as possible, but as soon as you're using .ToList(), then all elements are created in memory. LINQ tries to optimize performance this way behind the scenes.

DotNetFiddle of all examples

@Ian P´s answer helped me a lot to understand yield and why it is used. One (major) use case for yield is in "foreach" loops after the "in" keyword not to return a fully completed list. Instead of returning a complete list at once, in each "foreach" loop only one item (the next item) is returned. So you will gain performance with yield in such cases. I have rewritten @Ian P´s code for my better understanding to the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YieldReturnTest
{
    public class PrimeFinder
    {
        private Boolean isPrime(int integer)
        {
            if (0 == integer)
                return false;

            if (3 > integer)
                return true;

            for (int i = 2; i < integer; i++)
            {
                if (0 == integer % i)
                    return false;
            }
            return true;
        }

        public IEnumerable<int> FindPrimesWithYield()
        {
            int i;

            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    yield return i;
                }
            }
        }

        public IEnumerable<int> FindPrimesWithoutYield()
        {
            var primes = new List<int>();
            int i;
            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    primes.Add(i);
                }
            }
            return primes;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PrimeFinder primes = new PrimeFinder();

            Console.WriteLine("Finding primes until 7 with yield...very fast...");
            foreach (int i in primes.FindPrimesWithYield()) // FindPrimesWithYield DOES NOT iterate over all integers at once, it returns item by item
            {
                if (i > 7)
                {
                    break;
                }
                Console.WriteLine(i);
                //Console.ReadLine();

            }

            Console.WriteLine("Finding primes until 7 without yield...be patient it will take lonkg time...");
            foreach (int i in primes.FindPrimesWithoutYield()) // FindPrimesWithoutYield DOES iterate over all integers at once, it returns the complete list of primes at once
            {
                if (i > 7)
                {
                    break;
                }
                Console.WriteLine(i);
                //Console.ReadLine();
            }

            Console.ReadLine();
            Console.ReadLine();
        }
    }
}

What does the method you're using this in look like? I don't think this can be used in just a loop by itself.

For example...

public IEnumerable<string> GetValues() {
    foreach(string value in someArray) {
        if (value.StartsWith("A")) { yield return value; }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top