IEnumerable does not guarantee that enumerating twice will yield the same result. In fact, it’s quite easy to create an example where myEnumerable.First() returns different values when executed twice:
class A {
public A(string value) { Value = value; }
public string Value {get; set; }
}
static IEnumerable<A> getFixedIEnumerable() {
return new A[] { new A("Hello"), new A("World") };
}
static IEnumerable<A> getDynamicIEnumerable() {
yield return new A("Hello");
yield return new A("World");
}
static void Main(string[] args)
{
IEnumerable<A> fix = getFixedIEnumerable();
IEnumerable<A> dyn = getDynamicIEnumerable();
Console.WriteLine(fix.First() == fix.First()); // true
Console.WriteLine(dyn.First() == dyn.First()); // false
}
This is not just an academic example: Using the popular from ... in ... select new A(...) will create exactly this situation. This can lead to unexpected behaviour:
fix.First().Value = "NEW";
Console.WriteLine(fix.First().Value); // prints NEW
dyn.First().Value = "NEW";
Console.WriteLine(dyn.First().Value); // prints Hello
I understand why this happens. I also know that this could be fixed by executing ToList() on the Enumerable or by overriding == for class A. That’s not my question.
The question is: When you write a method that takes an arbitrary IEnumerable and you want the property that the sequence is only evaluated once (and then the references are “fixed”), what’s the canonical way to do this? ToList() seems to be used mostly, but if the source is fixed already (for example, if the source is an array), the references are copied to a list (unnecessarily, since all I need is the fixed property). Is there something more suitable or is ToList() the “canonical” solution for this issue?
ToListis very definitely the way to go.It has some optimisations: if the input enumeration is an
ICollection(e.g. an array or list) it will callICollection.CopyToto copy the collection to an array rather than actually enumerating – so the cost is unlikely to be significant unless the collection is enormous.IMHO, in general it is better for most methods to return
ICollection<T>orIList<T>rather thanIEnumerable<T>, unless you want to hint to consumers of the method that the implementation may use lazy evaluation (e.g. yield).In cases where a method should return an immutable list, return a readonly wrapper (
ReadOnlyCollection<T>) e.g. by callingToList().AsReadOnly(), but still return the interface typeIList<T>.If you follow this guideline, consumers of the method won’t ever need an unnecessary call to
ToList.