views:

193

answers:

4

Simple example - you have a method or a property that returns an IEnumerable and the caller is iterating over that in a foreach() loop. Should you always be using 'yield return' in your IEnumerable method? Is there ever a reason not to? While I understand that it may not always be necessary to, or even "better" (maybe it's a very small collection for example), is there ever a reason to actively avoid doing this?

The bit of code that got me thinking about this was a function I wrote very similar to the accepted answer in this thread - http://stackoverflow.com/questions/1847580/how-do-i-loop-through-a-date-range

+9  A: 

Iterator blocks perform a "live" evaluation each time they are iterated.

Sometimes, however, the behavior you want is for the results to be a "snapshot" at a point in time. In these cases you probably don't want to use yield return, but instead return a List<> or Set, or some other persistent collection instead.

It's also unnecessary to use yield return if you're dealing with query objects directly. This is often the case with LINQ queries - it's better to just return the IEnumerable<> from the query rather than iterating and yield returning results yourself. For example:

var result = from obj in someCollection
             where obj.Value < someValue
             select new { obj.Name, obj.Value };

foreach( var item in result )
   yield return item; // THIS IS UNNECESSARY....

// just return {result} instead...
LBushkin
+2  A: 

Odd question. If your method is returning an IEnumerable that it gets from somewhere else, then obviously it won't be using yield return. If your method needs to assemble a concrete data structure representing the results in order to perform some manipulation on it before returning, then I guess you won't use yield return there either.

mquander
A: 

I don't think so. As @LBushkin suggests, if you were going to return something as a whole, you'd return an IList or whatever. If you're returning an IEnumerable, people expect deferred execution, so I think you should always use yield in that case.

Grant Crofton
+3  A: 

One clear reason to not use an enumerator is when you need IEnumerator<>.Reset() to work.

Iterators are very nice, but they can't escape the "there's no free lunch" principle. You won't find them used in the .NET framework collection code. There's a good reason for that, they can't be as efficient as a dedicated implementation. Now that mattered to the .NET designers, they couldn't predict when efficiency matters. You can, you know whether your code is in the critical path of your program.

Iterators are a bit over twice as slow as a dedicated implementation. At least that's what I measured by testing the List<> iterator. Watch out for micro optimizations, they are still really fast and their big Oh is the same.

I'll include the testing code so you can verify this for yourself:

using System;
using System.Collections.Generic;
using System.Diagnostics;

class Program {
    static void Main(string[] args) {
        var lst = new MyList<int>();
        for (int ix = 0; ix < 10000000; ++ix) lst.Add(ix);
        for (int test = 0; test < 20; ++test) {
            var sw1 = Stopwatch.StartNew();
            foreach (var item in lst) ;
            sw1.Stop();
            var sw2 = Stopwatch.StartNew();
            foreach (var item in lst.GetItems()) ;
            sw2.Stop();
            Console.WriteLine("{0} {1}", sw1.ElapsedMilliseconds, sw2.ElapsedMilliseconds);
        }
        Console.ReadLine();

    }
}

class MyList<T> : IList<T> {
    private List<T> lst = new List<T>();

    public IEnumerable<T> GetItems() {
        foreach (T item in lst)
            yield return item;
    }

    public int IndexOf(T item) { return lst.IndexOf(item); }
    public void Insert(int index, T item) { lst.Insert(index, item); }
    public void RemoveAt(int index) { lst.RemoveAt(index); }
    public T this[int index] {
        get { return lst[index]; }
        set { lst[index] = value; }
    }
    public void Add(T item) { lst.Add(item); }
    public void Clear() { lst.Clear(); }
    public bool Contains(T item) { return lst.Contains(item); }
    public void CopyTo(T[] array, int arrayIndex) { lst.CopyTo(array, arrayIndex); }
    public int Count { get { return lst.Count; } }
    public bool IsReadOnly { get { return ((IList<T>)lst).IsReadOnly; } }
    public bool Remove(T item) { return lst.Remove(item); }
    public IEnumerator<T> GetEnumerator() { return lst.GetEnumerator(); }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
Hans Passant