tags:

views:

1165

answers:

7

In my everlasting quest to suck less I keep bumping into the "yield" statement.
I have tried to wrap my head around it a few times now, but I'm stumped every time with the same error.

It goes a little something like this:

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 last got stuck:

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

What am I doing wrong? Can't I use the yield in an iterator? Then what's the point? In the case 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, not?

--Edit-- Thanks for all the quick answers. Answer given to first answer. I know this question isn't all that new and the same resouces keep popping up. It turned out I was thinking I could return List as a return type, but off course because List isn't lazy, it cannot be used. Changing my return type to IEnumerable solved the problem :D

Now a somewhat related question (not worth opening a new thread): is it worth giving IENumerable as a return type if I'm sure that 99% of the cases I'm going to go .ToList() anyway? What will performance do for me there?

+14  A: 

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.

Lasse V. Karlsen
For the record: or IEnumerator[<T>]
Marc Gravell
It can also be declared to return IEnumerator or IEnumerator<T>.
Jon Skeet
Darn it, beaten by 7 seconds ;)
Jon Skeet
A: 

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; }
    }
}
Hugoware
+1  A: 

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();
        }
    }
}
Ian P
+4  A: 

"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));                
    }
}
Marc Gravell
Thanks! It thought that List<T> implemented IEnumerable, but apparantly it doesn't.. Now does it make sence to go the extra yield mile if you have to go SomeMethod().toList() a few lines further down the road? If my understanding serves me right, it defeats the whole purpose of yield, right?
borisCallens
@boris - List<T> does implement IEnumerable - but that isn't to point. To create an iterator block you *must* return either the ienumerable/ienumerator interface itself. It isn't defined for anything else.
Marc Gravell
@boris - re the "defeats the whole purpose" - not at all ;-p There are a lot of uses where a streaming API (such as IEnumerable<T>) is preferable to a buffered collection (such as List<T>) - especially if you are dealing with many thousands of records (from a file or database).
Marc Gravell
But that would kill the lazyness of my method. Not?
borisCallens
Sorry, re-read your answer and what you're saying is you should decide depending on the situation if you want it lazy or not. This brings me to my 2ndary question: if I know that I'm going to use ToList in 90% of the time, is it still better to return IENumerable and then ToList() it later?
borisCallens
@boris - very little to choose between them... if you expose IEnumerable<T>, you at least have the *option* to be lazy, but in reality it probably isn't going to hurt hugely.
Marc Gravell
+4  A: 

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:

Jon Skeet
A: 

This almost identical question has a link to some good Raymond Chen stuff:

http://stackoverflow.com/questions/39476/what-is-the-yield-keyword-used-for-in-c

Will Dean
A: 

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

Simon Steele