IReadOnlyCollection instead of IEnumerable

Whenever you expose a service API, either internally to your team, or publicly in a framework, such as with an interface or simply in a method signature and you want the client to pass or receive a collection of objects you have multiple choices: List<T>, array of T, IEnumerable<T>, and a more.

For simplicity, let’s assume you just have a method that wants to return such a collection. What do you do about it? What type do you use instead for Collection placeholder?

public Collection<T> Get() { ... }

In many cases I saw developers enjoy using IEnumerable<T> to represent that Collection type, because – in their opinion – is the interface that is at the lowest level and guarantees that (unless casting on the client side) the collection they return remains the same (i.e. the client code cannot modify the collection).

public IEnumerable<T> Get() { ... }

But for the client developer IEnumerable<T> may signal something totally different as well: that the service doesn’t actually return the data they want but only a promise to be able to get the data eventually when trying to browse the “collection”. This would automatically raise awareness that it could turn into a performance issue as looping the collection multiple times would actually invoke multiple yield return calls internally within your service implementation. So the client developers will usually to a .ToArray() themselves to cache results and ensure best performance of their apps.

So then, is it better to expose an array of T instead of IEnumerable<T>? Not very often, either! This is because the array elements could then be changed by the client code, since an array object has a public item setter (indexer):

public T[] Get() { ... }
...
T[] ts = Get();
ts[0] = anotherT;

And this could be a serious issue as collection instances that should be internal to the service could be easily updated by the client that received the array reference. (Of course, passing the collection as an IList<T> is even worse, since then you also give the client developer two more methods to work with: Add and Remove.)

In the latest .NET Framework versions, however, you could use a better, intermediary, interface instead, IReadOnlyCollection<T>:

IReadOnlyCollection<T> Get();

That would clearly signal to the client developer that:

  • The data in the collection is already prepared by your service, and no cache is necessary to browse the collection multiple times;
  • Collection should be treated as immutable, i.e. the client code should only read it, and not update it; you can also use ReadOnlyCollection<T> class internally in the service implementation to host the items if you want to avoid casting of the returned object to a mutable collection on the client side;
  • There would be no issue if the data would be accessed later, such as after a connection is closed and the original promise of IEnumerable<T> would otherwise be compromised. 🙂

Of course, sometimes IEnumerable<T> is really needed: that is, when you really do yield return calls and provide items one by one, and you want that the client developer to be able to stop your loop prematurely; or when you already have the IEnumerable reference from another (external) place and you don’t know how it’s done (with yield return or returning a cached collection) and don’t want to cache data yourself.

But in (all) other cases, I would strongly suggest to always use the new interface – IReadOnlyCollection<T> – with it your service interface becomes a lot more expressive and easier to understand. (In my opinion, this interface should have been there and promoted aggressively since .NET 2.0!)

And what if you have to receive a collection as an input parameter for your method?

public Action(Collection<T> input);

You could also decide to receive IReadOnlyCollection<T> to allow your clients to pass the already cached data if they have it, or else they can simply do a .ToArray() on their enumerable to access your service. And then you can avoid caching the data from the enumeration yourself. But on input types I personally don’t think using IReadOnlyCollection<T> instead of IEnumerable<T> is that important. If you really want your client developers to be able to pass all types of enumerations as input to your service without any ToArray calls on their side, it’s fine, and possibly cleaner. Just ensure that you cache data internally in your service implementation if you browse the collection multiple times. (But don’t request arrays or lists unless you need to change the item references or add/remove items in your service – otherwise the client developer won’t know you don’t do it.)

Advertisements

About Sorin Dolha

My passion is software development, but I also like physics.
This entry was posted in .NET, C# and tagged , , , , , , , . Bookmark the permalink.

One Response to IReadOnlyCollection instead of IEnumerable

  1. Jim says:

    I couldn’t resist commenting. Very well written!

    Like

Add a reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s