tags:

views:

95

answers:

2

Take the example here:

 public static IEnumerable<BigInt> EvenNumbers(IEnumerable<BigInt> numbers)  
 {  
     foreach (BigInt number in numbers)  
     {  
         if (number % 2 == 0)  
         {  
             yield return number;  
         }  
     }  
 }

This will return only the values which match the criteria (n % 2 == 0). But what is the difference between yield return number; and return number;?

If I say yield return number, will it return each number to the calling function and so on? Where can I find some details on what goes on behind the scenes?

Thanks

+5  A: 

"return" simply won't work here (since it would try to return a BigInt, and the method declares an IEnumerable<BigInt>. Jon Skeet has a good write-up of iterator blocks (what this is) in the free chapter 6 of C# in Depth (then buy the whole book - it really is worth it ;-p).


edit - here's a very rough version of what you would have to do in order to write this yourself; note that it doesn't quite do the same, but achieves the goal. I think you'll agree that the yield return version is easier!

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
static class Program
{
    static void Main()
    {
        IEnumerable<int> source = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        foreach (int value in EvenNumbers(source))
        {
            Console.WriteLine(value);
        }
    }

    public static IEnumerable<int> EvenNumbers(IEnumerable<int> numbers)
    {
        return new EvenEnumerable(numbers);
    }
    class EvenEnumerable : IEnumerable<int>
    {
        private readonly IEnumerable<int> numbers;
        public EvenEnumerable(IEnumerable<int> numbers) {
            this.numbers = numbers;
        }
        public IEnumerator<int> GetEnumerator()
        {
            return new EvenEnumerator(numbers);
        }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
    class EvenEnumerator : IEnumerator<int>
    {
        private readonly IEnumerable<int> numbers;
        public EvenEnumerator(IEnumerable<int> numbers)
        {
            this.numbers = numbers;
        }
        private int current;
        void IEnumerator.Reset() { throw new NotSupportedException(); }
        public int Current { get { return current; } }
        object IEnumerator.Current { get { return Current; } }
        IEnumerator<int> iter;
        public bool MoveNext()
        {
            if (iter == null) iter = numbers.GetEnumerator();
            while (iter.MoveNext())
            {
                int tmp = iter.Current;
                if (tmp % 2 == 0)
                {
                    current = tmp;
                    return true;
                }
            }
            return false;
        }
        public void Dispose()
        {
            if (iter != null)
            {
                iter.Dispose();
                iter = null;
            }
        }
    }
}
Marc Gravell
Thanks for that. I've read Skeet's site, I will compliment that by going through his book. Should help a great deal.
dotnetdev
+1  A: 

Using yield return actually causes the compiler to create a simple IEnumerator class that does the work of the loop. The structure of the for loop actually determines the operations inside the MoveNext method that the compiler creates.

Check out this blog post for sample IL deconstruction

BC
Funnily enough, I read that link. I have a much better understanding now. If I call a method involving yield return (well an iterator block) and returning IEnumerable, every time yield return is hit, control is returned to the calling function. Can I only use yield return with IEnumerable?
dotnetdev
@GSS - the method must return IEnumerable, IEnumerable<T>, IEnumerator or IEnumerator<T> to use yield return.
Marc Gravell