Why doesn’t C#’s IList contain AddRange?

Ok, I’ll admit it. I did a bit of Java back in the day. To be precise, I learn’t a little Java in order to teach it to a student who was doing a Business with Computing degree. I was basically keeping one week ahead of the guy’s lectures so to give him the extra help he needed. From this student’s course, there are two things that will remain with me until the day I die:

  • Java loves classes in classes in classes in …,
  • Programme to interfaces.

Having cycled from education to science to IT, I am now a professional C# developer, trying to enact this second piece of advice in my day-to-day work (my co-workers get grumpy if I start using sub classes in C#).

Although writing

probably has no real benefits over declaring theList as a List<string>, I personally believe that it is probably a good habit to get into, especially when you start using dependency injection containers. The dependency injection packages I have used all work via the DI container injecting a concrete class into a interface — therefore the developer builds each classes dependencies to be interfaces and programmes against these. I have therefore got used to programming against interfaces rather than concrete implementations.

This however raises one interesting quirk. Take the following code that looks great, but wont compile:

This could be used to get a summary box on a checkout page of all discounts applied to an order. It builds a IList<string> as it needs to add to this collection but returns an IEnumerable<string> indicating to the developer that the list is intended to be read, not added to.

Unfortunately, dear Rosilyn, our friendly C# compiler, throws a wobbly at this point complaining that:

Apparently, IList<> does not contain an AddRange() method, however, List<> does. Why?

Although I am not an authoritative source, this is my view as to the philosophical reasoning as to why IList<> does not contain an AddRange() method.

Interfaces can be described as a promise to be able to do certain things. We often come across two distinct types of interface in C#,

  • The full-class interface — where every method on a class is represented on it’s interface. This allows the class to be injected into another class via a Dependency Injection container, or a mock version used by a class undergoing a Unit Test.
  • The class-functionality interface — e.g. IDisposable or IEnumerable<> which indicate that a class as specific functionality available. Every IEnumerable<> is guaranteed to to be usable in a foreach loop.

Contrary to what its name suggests, IList<> is not a full-class interface of List<>, rather a stand-alone functionality interface that has little to do with the specific List<> class. It belongs to the inbuilt hierarchy of C# collection interfaces which provide a “ladder” of functionality:

  1. IEnumerable<>: Class can be iterated over.
  2. ICollection<>: As IEnumerable<>, plus class can have items added and removed from it. Also has a Count property and a Contains() method.
  3. IList<>: As ICollection<>, plus items can be accessed with array-like syntax (myList[2]). Also has a IndexOf() method.

IDictionary<> sits alongside IList<> and there is a similar hierarchy of IEnumerable<> > IReadOnlyCollection<> > IReadOnlyList<>.

Now, if rather than considering IList<> to be the interface for List<>, you consider List<> to be a default Microsoft-supplied implementation of the IList<> interface you can start to understand why List<> may have more functionality.

When creating a custom implementation of a collection of objects, what functionality do you want to “have to” implement? IList<> states that you have to be able to add and remove items — it doesn’t force you to implement a method to add a collection of objects, in the same way that it doesn’t force you to have a method to remove every third item or sort every item into alphabetical order according the the Sanskrit alphabet. There was a line to be drawn at what functionality was deemed necessary for all IList<> implementations and what was “nice-to-have” or “specialised”. The creators of C# drew this line at Add(), InsertAt(), Remove(), RemoveAll(), RemoveAt(), Clear(), Count and Contains().

However, when creating a generic List<> implementation to be used, they added other helpful methods that, while not needed in every application, save developers from having to build their own versions of fairly common functionality. There are a large number of other methods they also implemented: Reverse() , Sort() , TrimExcess() and all manner of Find… methods.

There you have it. List<> and IList<> are fundamentally different things, built for different purposes. IList<> could have had a AddRange() method, but the creators decided to draw the line at what was absolutely needed for a list-type construct and save developers creating custom IList<> implementations a few lines of code. The List<> class was created as a fully functioning and functional object container.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jonathan Twite

Astrophysicist turned full-stack .NET jack-of-all-trades master-of-none.