Search code examples
c#coding-styleienumerableilistfunction-calls

return IEnumerable and pass as agument


I read a lot about why it is better to return an IEnumerable instead of an IList (or something like this). But now I stuck at some point.

Consider something like this: I have some functions that do multiple enumerations and therefore take an IReadOnlyCollection as a parameter, create a new List and return it as an IEnumerable.

IEnumerable<int> Foo1(IReadOnlyCollection<int> bar)
{
   bar.Any();
   bar.First();
   return new List<int>() {1, 2, 3};
}
IEnumerable<int> Foo2(IReadOnlyCollection<int> bar)
{
   bar.Any();
   bar.First();
   return new List<int>() {1, 2, 3};
}
IEnumerable Foo3 ...

Now if I want to call them one after another I have to do something like this

var tmp = Foo1(new List<int>() {1, 2, 3});
tmp = Foo2(tmp.ToList());
tmp = Foo3(tmp.ToList());
tmp = Foo4(tmp.ToList());
tmp = Foo5(tmp.ToList());

That means I have to create a 11 lists. The first one, one in every function and one for every function call. But in fact it would be enough to create 6 lists. Is there a better way or is this simply a possible drawback when returning IEnumerable?


Solution

  • In general, this is simply a tradeoff inherent in returning IEnumerable.

    The advantage of returning IEnumerable is that you can change your Foo() implementation to return nearly any type of collection (or even a lazy sequence with yield return or LINQ) without changing the API. When creating an interface, it's important to balance this advantage against the fact that consumers of an IEnumerable API who want to enumerate it multiple times will always have to convert it to a collection.

    Even more generally, returning a more specific type (e. g. List over IList) is most useful to consumers and most challenging in terms of maintaining the API. Some things to consider:

    • Is there any real use-case for returning a lazily-evaluated sequence? If not, consumers will likely appreciate getting a collection type instead of IEnumerable
    • Is this order of your result set important? In this case, List, IList, or IReadOnlyList is often preferable over something like ICollection or IReadOnlyCollection?
    • Is the returned collection guaranteed to be newly generated or might it be cached / a view over internal data? If the latter? If you could see converting to the latter pattern in the future, then you'll want to be sure to return a read-only interface or collection type.

    If you're just worried about the performance of creating new lists (which shouldn't be a real concern in nearly all cases), you can always create an extension as follows:

    public static IReadOnlyCollection<T> AsReadOnlyCollection<T>(this IEnumerable<T> @this)
    {
        return @this as IReadOnlyCollection<T> ?? @this.ToArray();
    }