.net, C# tip, Clean Code

C# tip – When to return IEnumerable instead of IList (and when not to)

I think it’s now almost mandatory for every tech blog to deal with this question.

There’s a received wisdom that it’s always better to return the most specific interface – meaning the interface which has the smallest possible set of functions. By that token, since IEnumerable<T> is smaller than IList<T> you should return IEnumerable<T>. You can’t predict that the user will need to use things that IList<T> offers (like the Count() method) because you don’t know that they’re going to need it.

I can see how this makes sense.

There’s also a counterpoint that says you should return the most functionally rich interface – so instead of returning an interface which has nothing but GetEnumerator, give your consumer something that will be immediately useful to them – for example, IList has a Count() method, or allows them to retrieve elements at a specific index. It’s cheap for the developer and it might add value for the user.

Whatever is right for you, is right for you – most articles that I’ve read come down on the site of IEnumerable<T>, but it’s not cut and dried.

My personal preference is to go with the smallest possible interface. It makes sense for me because if I need to change my return type later, it’s easy to go from IEnumerable<T> to (say) ICollection<T> and know my public API consumer’s code will continue to work.

That logic doesn’t work the other way around. Say I have the following code:

private static IList<SelectListItem> ConvertListItemsToSelectList(IEnumerable<User> users)
{
    var userSelectList = new List<SelectListItem>();
 
    foreach (var user in users)
    {
        userSelectList.Add(
            new SelectListItem { 
                Value = user.Id.ToString(), 
                Text = user.Name 
            });
    }
 
    return userSelectList;
}

At some future point, I may want to change this code to use the yield keyword, as below:

private static IEnumerable<SelectListItem> ConvertListItemsToSelectList(IEnumerable<User> users)
{
    foreach (var user in users)
    {
        yield return new SelectListItem { 
            Value = user.Id.ToString(), 
            Text = user.Name 
        };
    }
}

The code is shorter, cleaner, declares fewer variables, and frustratingly I can’t use it because I have to change my return type to IEnumerable<SelectListItem>. If one of my users has performed a Count() operation on the original version of ConvertListItemsToSelectList, their code will no longer compile with my latest change. Going for IEnumerable<SelectListItem> in the first place would have been a better choice.

I guess if it was a privately used API, which I knew was never going to change, I might feel differently. As with so much in software craftsmanship, it’s about picking the right tool for the job at hand.

You can read more about the IList interface on MSDN here.

You can read more about the ICollection interface on MSDN here.

You can read more about the IEnumerable interface on MSDN here.