My recent experiments with LINQ have taken me to rediscover the power of the IEnumerable interface in .NET. As many of you know by now, one of the cornerstones on which LINQ is built and that makes it so accessible is that it fully supports the very basic IEnumerable interface.

This is extremely powerful because it means the barrier to entry to play in the LINQ world is very low: Pretty much all typed containers in .NET support IEnumerable in one way or another, which means that if you're using any of them then you're ready to start using LINQ and take advantage of its powerful query capabilities. Obviously, for more advanced scenarios, LINQ does define a more powerful mechanism in the IQueryable interface.

However, the goodness of IEnumerable does not start or stop with LINQ. It's far better than that, because you can take advantage of its properties right now. The primary benefits of the IEnumerable interface are two:

  1. It enables a very simple and useful language construct: The "for each" loop.
  2. It is a highly restricted interface.

The second property is the key that makes IEnumerable so nice to use on your designs: Because it serves pretty much only one purpose (to allow iteration), it means that your purposes with regards to the interface are extremely clear to users of your API, and thus allows them more flexibility. It might sound strange at first that using a more restrictive interface leads to more flexibility, so let me explain what I mean.

When you're exposing .NET collections directly as part of your API interface, either as properties or as arguments or returns values of functions, you're encouraged to prefer using one of the base collection interfaces instead of the collection types by tools like FxCop [1] (except if you're using arrays directly). So, for example, instead of using List, you're encouraged to use IList; instead of using Dictionary, you're encouraged to use IDictionary instead.

This is a good practice because it gives you some flexibility in switching collection implementations later on if your original choice wasn't the right one, and I've made a habit of trying to follow it whenever I remember to do so (and FxCop is always there to remind me, as well). However, for many scenarios, even the base interfaces are way to open. For a given scenario, you really have to ask yourself: Do I really need the full capabilities expressed by an interface like IList?

For collection properties, I would say that most of the time you do actually need them, so I won't consider them here. However, for method arguments and return values, that's actually more debatable. A lot of scenarios don't require all those capabilities, and indeed, just the capability to iterate over the collection is more than enough! Those are the scenarios were you will want to restrict your public interface and just use IEnumerable instead.

Method Arguments

If all you want to do is walk over a collection inside one of your methods, then by all means you'll want to restrict your interface and ask for an IEnumerable instead:

void DoSomething(IEnumerable<X> list) {

}

As expressed, the interface for the method presents a clearer intent. For the method implementor, it says: All you're supposed to do with this collection is iterate over it. For the method user, it gives him a lot more flexibility in choosing what he passes in, and gives him a clear idea of what the method does with the collection.

This is possibly the case where you more often will want the restricted interface because you, as the developer writing the method, have full control and knowledge about the intent.

Method Return Values

If you're defining a method that will return a collection, think what you expect the user to do with the collection you return. Normally, returning the full collection interface here is the right thing to do because if you're returning a "free flying" (i.e. detached) collection, you want your client to be able to do with it whatever it pleases after that.

However, in some scenarios the opossite is true. Sometimes you get to define a method on which you'll be the consumer and not the implementor. For example, if you're defining an interface to act as an extension hook into your own library/application, then you get to design that interface and your own code will consume it, but someone else is going to be implementing it. In those cases, then you can apply the same reasoning as the "Method Arguments" case above! Since you're the consumer and you have intimate knowledge of what you'll do with the collection you get, you can restrict it to IEnumerable and thus give more flexibility to those that are going to be implementing your extension interface.

public IEnumerable<X> GetAll(){

}

One of the nice side effects of having a method defined as returning IEnumerable instead of a collection is that however implements it might even do away with returning a collection at all. This is particularly nice because you can then implement the method using the Iterators feature in C# 2.0 [2] with the "yield return" statement instead of manually building a collection and returning it. This can also lead to memory savings under the right circumstances.

[1] Actually, FxCop does give you a choice: Either use the collection interfaces, or use the collection types in System.Collections.ObjectModel.

[2] It might not be evident at first look, but you can define iterator methods as returning either an IEnumerator or an IEnumerable. Juwal Lowy has a good discussion of iterators in his MSDN Magazine Article.

Technorati: , ,


Tomas Restrepo

Software developer located in Colombia.