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
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 return
ing 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...
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.
Without reading through answers I will tell you that using yield in a WCF service will mess up your FaultException!
It took me quite some time to find out that yield was the culprit. I just needed a fast way too get som errors up n running. The proper way would of course be to include an Error object in the reply but I needed it fast. I did : throw new FaultException("No subscription with id: " + subscriptionId.ToString());
But using yield, even below the actuall throwing, caused a regular exception, network
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(); }
}
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.