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#).
IList<string> theList = new List<string>();
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:
public IEnumerable<string> GetBasketDiscounts()
IList<string> allDiscounts = new List<string>();
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:
Error CS1061 ‘IList<string>’ does not contain a definition for ‘AddRange’ and no accessible extension method ‘AddRange’ accepting a first argument of type ‘IList<string>’ could be found (are you missing a using directive or an assembly reference?)
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
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.
IEnumerable<>which indicate that a class as specific functionality available. Every
IEnumerable<>is guaranteed to to be usable in a
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:
IEnumerable<>: Class can be iterated over.
IEnumerable<>, plus class can have items added and removed from it. Also has a
Countproperty and a
ICollection<>, plus items can be accessed with array-like syntax (
myList). Also has a
IDictionary<> sits alongside
IList<> and there is a similar hierarchy of
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
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:
TrimExcess() and all manner of
There you have it.
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.