Iris Classon
Iris Classon - In Love with Code

Stupid question 41: Should I use IList or List?

[To celebrate my first year of programming I will ask a ‘stupid’ questions daily on my blog for a year, to make sure I learn at least 365 new things during my second year as a developer]

Should I use List or IList?

Out of habit I’ve been using interfaces when working with collections, using the one with bare minimum of what I need. Except for the constant reminders from refactoring tools I might have installed, I also have a vague recollection of being told to do so by a skilled developer when I did my first (kind of) internship. But a few days ago this question came up at work, and I thought it was about time to check whether my thinking around this was right or not. So I spent a little time finding out, should I use an interface or the concrete implementation?

So, again, as with the last discussion - I’ll gladly change my mind if I am wrong on this one so please give me those really valuable comments below!

I always choose to use the interface with the bare minimum that I need, I rarely use IList, IEnumerable seems to be my favorite, but there are times when I do need the extra functions that come with IList. And I know this is something not everybody agrees on, but in a method I tend to return the same thing as I take in when it comes to collections. In other words, if I take an IEnumerable I’ll return an IEnumerable. I do this even in the smallest of projects, it’s just a habit I’ve gotten into and I like to be consistent.

But I might be wrong on this one, so I’m passing the question to you, IList or List? IEnumerable or IList? And does the type of project matter (exposed vs unexposed code)?

Comments

Leave a comment below, or by email.
Fredrik Mörk
9/10/2012 11:15:47 PM
I tend to agree with you; I always try to use an interface over a concrete class in method signatures, and usually try to go for the "smallest" possible interface. In the question of IEnumerable vs. IList, especially when talking about method parameters, there is an important communication that is made. If a method takes an IEnumerable, I see that as a contract where the method says "I will not alter the content of your sequence", while in the case of IList, the contents of the collection may have changed when the method call returns.

So, I will choose IEnumerable over IList, if it does the job. Otherwise I'll choose IList over List. 
TheCodeJunkie
9/10/2012 11:17:25 PM
You should always choose the least specific abstraction, because it gives you the luxury of changing the underlying implementation without breaking your public API. I find that, for the most part, this means choosing IEnumerable.

The abstraction you choose also express the intent of your API - i.e how is it supposed to be consumed? Are you, as the API developer, expecting people to add to the collection that is returned. And even if you do expect it, is it your concern HOW they add to that list?

The consumer of your API can always wrap the IEnumerable into a collection themselves and with the introduction of the .ToList and .ToArray LINQ extension methods this was made even more trivial.

When dealing with method parameters you need to think twice because choosing to use IList over IEnumerable (even though you need to add to the provided collection) does reduce the set of collections that can be passed in.. not all collections implements the IList interface, but all do implement the IEnumerable 
Michael Lund
9/10/2012 11:22:05 PM
When you take the bare minimum (ie. IEnumerable) you ensure that your method will be usefull for any collection type implementing that interface. The stricter you get in your parameters, the smaller the audience for your method. Therefore only be as specific as you have to.
Further: Using interfaces instead of classes, will often make it easier to create mocks when unit testing.

When you want to return the same type that you get as parameter, consider using generics:

public T MyMethod(T collection) where T : IEnumerable
{
}

This will enusre that if the caller give your method a List the caller will also get a List back. 
Simon C
9/11/2012 12:03:06 AM
I totally agree with you Iris, A+ from me :P 
Martin Suchan
9/11/2012 1:25:51 AM
In classes you want to serialize using XmlSerializer od DataContractSerializer you can't use interface types, because they can't be serialized. In the other classes it's OK to use interface types and in case you want to add other functionality to lists or other collections, you can use extension methods. 
John
9/11/2012 3:58:08 AM
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

I would avoid using either List/IList, t.b.h. If exposed publicly, it will make it possible to alter the contents of the list outside the class. IEnumerable all the way. 
Petr Šrámek
9/11/2012 5:14:40 AM
It always depends on purpose. In the text below I will talk about generic interfaces and types. Non generic are similar, but not the same. There are some differences.

When you need only to iterate through collection you might use IEnumerable interface. IEnumerable is implemented in all (for me known) collection types. IEnumerable is not inherited from any other interface so we can say it is base interface.

MSDN - Generic IEnumerable Interface

When you need to use Count property or basic methods like Contains, Add, Remove or Clear on collection you should use ICollection interface cause it defines these. ICollection is inherited from IEnumerable, so it extends its behavior. I use it when I need to manipulate with items inside the collection.

MSDN - Generic ICollection Interface

When you need indexes you have to use IList interface, which give you the methods for it. IList is inherited from both above mentioned interfaces. Here it is about extending behavior too. I just use it when I need to use indexes and insert at some position in the collection.

MSDN - Generic IList Interface

I don't recommend to use concrete collection types. It is too hard to make change in application when you do. One exception is when you have your own conrete type which implements some different(or extends) behavior then IEnumerable, ICollection or IList and you need to use it at some application scope.

But still it is better to write interface and inherit concrete type from it and use that interface. Application parts stay independent and reusable. You don't have to rely on concrete implementation of the type. 
PPetrov
9/11/2012 6:55:10 AM
I'm using ICollection - it has Count property(it doesn't need to enumerate all items to find the count) & I can use(change to) list or an array or any ICollection if I have to. 
James Curran
9/11/2012 8:56:10 AM
It depends on where you are using it.  For incoming values (method parameters), use the minimal needed.  However, for outgoing values (properties & return values) use the maximum available (don't tell your clients  they only need the minimum).  For private fields, use a concrete type. 
James Tryand
9/11/2012 9:10:21 AM
+1 Michael Lund <- wot he said ;-) 
Scott Holodak
9/11/2012 10:05:21 AM
There are also occasionally security considerations.  In the same way that you would want to mark certain classes as sealed to prevent developers from being able to subclass them, you should pay attention to the collection types that you are returning in method call results and whether they are intended to be read-only or read-write.  

For instance, imagine an AuthenticationResult Authenticate(string username, string password) method.  Imagine the AuthenticateResult result class not only tells you whether the authentication succeeded, but also had a list of systems that the user is allowed to access.  If that list was defined as a List or an IList, the caller would have the ability to Add, Remove, and Clear items from the result after the call and could compromise the security of the system.  By defining the return type as IEnumerable instead, you are guaranteeing that the caller can read, but not modify the items in the result.  Similarly, IEnumerable<KeyValuePair> provides a read-only guarantee where IDictionary would not. 
Michael Lund
9/11/2012 12:28:55 PM
Reply to: PPetrov
An ICollection implements a Count property, but that doesn't tell you anything about how it is implemented. It is true for the collections provided by the framework (probably) that the collections knows the number of elements, but you don't know anything about how other ICollections implement the Count property. 
Further: If you call the Count() method on an IEnumerable, the Count() method checks if the collection is an ICollection and then uses the Count property, based on the assumption that it will be more effective. So the Count property should not be the deciding factor when chosing between IEnumerable and ICollection.

I actually wrote a blog post on LINQ and counting yesterday. 
Michael Lund
9/11/2012 12:44:00 PM
Reply to: Scott Holodak
Is that really true? I guess that if you return an IEnumerable to me the underlying type would still be for example List. So I could just cast it to List and do what I want.

I guess what you want is something like this (angle brackets are now "_of_T":

public IEnumerable_of_T myMethod()
{
 //do stuff initialize myList of type List_of_T
 return new ReadOnlyCollection_of_T(myList);
}

I don't know if ReadOnlyCollection prevents access to the underlying list through reflection. 
Scott Holodak
9/11/2012 2:03:46 PM
Reply to: Michael Lund
I wasn't really thinking about defending against malicious developers with access to the code, though you bring up a good point.  My thinking was that choosing restrictive interfaces would help steer developers away from misusing an API unknowingly in a way that could have unwanted side effects.  I guess 'guaranteeing' was too strong of word there.  Between reflection/emit, disassembly, and code weaving, I doubt there's any decent way to protect against truly malicious developers.  That said, I'm probably going to follow your lead with the ReadOnlyCollection/ReadOnlyDictionary wrappers around the fields. 
Petr Šrámek
9/12/2012 9:45:59 AM
Reply to: Michael Lund
I agree with Michael.

Count property is not main reason to use ICollection.

Deciders are Contains, Add, Remove and Clear methods. Use ICollection when You need these.

I do not completely agree with "but you don’t know anything about how other ICollections implement the Count property."

If you or somebody else write custom Count() extension method framework will use it. So, it is fifty-fity there will be some non sence counting which will hide IEnumerable.Count() extension method.. You have to trust developer knows what he is writing.

I rather use ICollection.Count property when I can(ICollection and all types/interfaces inherited from it). ICollection.Count property only give me what I need. IEnumerable.Count() method make double type check for generic and non generic ICollection is not null. 
Petr Šrámek
9/12/2012 10:31:19 AM
Reply to: Michael Lund
I write some code. Take a look how easy is to change Count() methods behavior by overriding/hiding IEnumerable.Count().

Collection.CountingI write some code. Take a look how easy is to change Count() methods behavior by overriding/hiding IEnumerable.Count().

Collection.Counting on GitHub

BTW. Michael: Your post about LINQ and Counting is great. 
Petr Šrámek
9/13/2012 3:47:53 AM
Reply to: Michael Lund
I think we are completely clear about it :) happy to talk about it ;) 
Michael Lund
9/13/2012 1:40:04 AM
Reply to: Petr Šrámek
Thank you.

Yes it is easy to override and do your own stuff - I get that.
My point is, I will always use Enumerable if I don't need the extra methods provided my ICollection. Count is not needed because the standard implementation of Count() will do the most optimal it can. (the small overhead of checking for null and if the collection is ICollection is really a non-issue).

If somebody implements a slow Count() method on their own in their IEnumerable collection, it is really their problem. It is not my job to protect them against that. An I still give those people the posibility to use my API. 

But I think we undertand each other - we just have a different point of view :-) 


Last modified on 2012-09-10

comments powered by Disqus