554

Can anyone explain to me why I would want to use IList over List in C#?

Related question: Why is it considered bad to expose List<T>

Community
  • 1
  • 1
Peanut
  • 18,967
  • 20
  • 72
  • 78
  • 8
    This question is not opinion-based. `List`, `IList`, and `T[]` are each more appropriate for different scenarios, and the difference is generally fairly objective. It is like [this other question](https://stackoverflow.com/questions/34334363/is-returning-ilistt-worse-than-returning-t-or-listt). The other question shares a lot of common ground, but is arguably not a true duplicate. In either case though, this is not opinion-based. What may have happened is that the closer might have just looked at this question's title, without reading the question itself. The body is objective. – Panzercrisis Apr 06 '21 at 18:49

18 Answers18

487

If you are exposing your class through a library that others will use, you generally want to expose it via interfaces rather than concrete implementations. This will help if you decide to change the implementation of your class later to use a different concrete class. In that case the users of your library won't need to update their code since the interface doesn't change.

If you are just using it internally, you may not care so much, and using List<T> may be ok.

Timwi
  • 65,159
  • 33
  • 165
  • 230
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 20
    I'm not understanding the (subtle) difference, is there some example you could point me to? – StingyJack Dec 30 '08 at 12:54
  • 152
    Say you had originally used List and wanted to change to use a specialized CaseInsensitiveList, both of which implement IList. If you use the concrete type all callers need to be updated. If exposed as IList the caller doesn't have to be changed. – tvanfosson Dec 30 '08 at 13:07
  • 57
    Ah. OK. I get that. Picking the lowest common denominator for a contract. Thanks! – StingyJack Dec 30 '08 at 13:34
  • 19
    This principle is an example of encapsulation, one of the three pillars of object-oriented programming. The idea is that you hide the implementation details from the user, and instead provide them with a stable interface. This is to reduce dependency on details that might change in the future. – jason Dec 30 '08 at 13:35
  • 14
    Note also that T[] : IList, so that for performance reasons you could always swap from returning a List from your procedure to returning a T[], and if the procedure is declared to return IList (or perhaps ICollection, or IEnumerable) nothing else would need to be changed. – yfeldblum Dec 30 '08 at 13:44
  • Microsoft .NET 4.0 API is using List instead of IList... I wonder why? http://msdn.microsoft.com/en-us/library/system.web.caching.ioutputcacheentry_properties(VS.100).aspx – JarrettV Jul 13 '09 at 00:17
  • 1
    For performance reasons, you don't want to access `T[]` via its `IEnumerable` interface, also arrays don't support e.g. `IList.Remove` – Ben Voigt Jul 27 '10 at 12:10
  • 2
    There is a rather odd LSP violation in the Array implementation in .Net, although Array implements IList calling the .Add(item) method on an IList whose concretion is an Array will result in an exception. Keep this in mind should you chose to use IList, in your contracts. – Gent Mar 13 '14 at 19:06
  • @Gent Array implements `IList`, not `IList` so I'm not sure I understand what you're saying. – tvanfosson Mar 13 '14 at 20:39
  • I'm sorry you are correct, it is IList, but the LSP violation still applies. – Gent Mar 14 '14 at 12:26
  • more information can be found here: http://stackoverflow.com/questions/11163297/how-do-arrays-in-c-sharp-partially-implement-ilistt – Gent Mar 14 '14 at 12:33
  • 1
    @jason _"Picking the lowest common denominator for a contract."_ is one of the best way to put it for the explanation of interface uses. Just Wow. – A. Go Dec 11 '20 at 17:35
354

The less popular answer is programmers like to pretend their software is going to be re-used the world over, when infact the majority of projects will be maintained by a small amount of people and however nice interface-related soundbites are, you're deluding yourself.

Architecture Astronauts. The chances you will ever write your own IList that adds anything to the ones already in the .NET framework are so remote that it's theoretical jelly tots reserved for "best practices".

Software astronauts

Obviously if you are being asked which you use in an interview, you say IList, smile, and both look pleased at yourselves for being so clever. Or for a public facing API, IList. Hopefully you get my point.

Community
  • 1
  • 1
Arec Barrwin
  • 61,343
  • 9
  • 29
  • 25
  • 70
    I have to disagree with your sardonic answer. I don't totally disagree with the sentiment of over-architecture being a real problem. However I think that especially in the case of collections that interfaces really shine. Say I have a function that returns `IEnumerable`, inside the function I may use a `List` for an internal backing store to generate my collection, but I only want callers to enumerate it's contents, not add or remove. Accepting an interface as a parameter communicates a similar message "I need a collection of strings, don't worry though, I won't change it." – joshperry Apr 23 '10 at 19:34
  • 45
    Software development is all about translating intent. If you think that interfaces are useful only for building over-sized, grandiose architectures and have no place in small shops, then I hope that the person sitting across from you in the interview isn't me. – joshperry Apr 23 '10 at 19:40
  • 1
    The use of IList instead of List doesn't even introduce any additional words in the code, so I can hardly believe an argument that it leads to unnecessary code. – recursive Jul 27 '10 at 12:03
  • 1
    @Arec Barrwin: You choose to expose List, then many clients consume that and depend on it. They decide to call methods on the List that don't exist on IList. Later you find using List causes problems with your algorithmic complexity, and you need to change the implementation of List. Have fun with that. – quentin-starin Nov 04 '10 at 22:23
  • 55
    Somebody had to say this Arec... even if you've got all sorts of academic pattern chasing hate mail as a result. +1 for all of us who hate it when a small app is loaded with interfaces and clicking on "find definition" takes us somewhere OTHER than the source of the problem... Can I borrow the phrase "Architecture Astronauts"? I can see it will come in handy. – Gats May 11 '11 at 20:55
  • 8
    If it would be possible, I will give you 1000 points for this answer. :D – Samuel Nov 16 '12 at 15:18
  • 1
    Not sure what this answer has to do with the question, which never mentioned writing your own implementation. Both IList and List are in the Framework; using List locks you to only one; using IList allows you to switch between Framework implementations (even if you never write your own). @Gats clicking on 'Find definition' for both List and IList will take you to the Framework, so I don't see how that is relevant either. – Sly Gryphon Feb 12 '13 at 02:50
  • 10
    I was talking about the wider result of pattern chasing. Interfaces for common interfaces good, extra layers for the sake of chasing a pattern, bad. – Gats Feb 13 '13 at 12:42
  • 1
    I think you're confusing over-architecting with good practices. I use interfaces because it makes my code testable. Being able to fake/mock a return to test out logic is important. It also depends on what you're building. If I'm building little console apps for utilities, I'll go straight List. Even if my code is only reusable within the confines of my organization (e.g., never go to the public web), I still use interfaces...for testing sake. – curiousdork Jun 19 '13 at 17:06
  • 2
    @DJSPIN80 Interfaces don't magically make your code more testable. I know what I just said is heresy, but you don't get anything for free just because it is an interface. – A.R. Oct 28 '14 at 15:17
  • 3
    @Gats from now on, we have "Go To Implementation" for that problem. :) – Burak Karakuş Jan 05 '16 at 16:39
  • 1
    I giggled. Thank you for the humor we all wish to be astronauts someday! Lets goto Mars!!! – JPK Sep 21 '16 at 10:27
  • 1
    Although it was difficult to print your post on a single page - it now hangs in my office on the wall (in full color). LOL Erik – Erik Nagel Aug 01 '17 at 07:59
  • 2
    The real problem is that `IList` doesn't approach the usability of `List`. `IList` is missing so much of the interface, sometimes it's hard to tell how `List` is even related to it. In other words, the interface `IList` is so minimalistic, it's nearly useless. Perhaps Microsoft should have named it `IIncompleteList`. – Suncat2000 Sep 23 '20 at 18:02
195

Interface is a promise (or a contract).

As it is always with the promises - smaller the better.

Rinat Abdullin
  • 23,036
  • 8
  • 57
  • 80
  • 65
    Not always better, just easier to keep! :) – Kirk Broadhurst Oct 05 '09 at 23:17
  • 7
    I dont get this. So `IList` is the promise. Which is the smaller and better promise here? This is not logical. – nawfal Jun 19 '14 at 06:28
  • 4
    For any other class I would agree. But not for `List`, because `AddRange` is not defined on the interface. – Jesse de Wit May 23 '17 at 17:31
  • 5
    This is not really helpful in this particular case. The best promise is the one that is kept. `IList` makes promises it can't keep. For instance, there is a `Add` method than can throw if `IList` happens to be read-only. `List` on the other hand is always writeable and thus always keeps its promises. Also, since .NET 4.6, we now have `IReadOnlyCollection` and `IReadOnlyList` which are almost always better fits than `IList`. And they are covariant. Avoid `IList`! – ZunTzu Nov 10 '17 at 13:33
  • @ZunTzu I'd argue that someone passing an immutable collection off as a mutable one has knowingly broken the contract presented -- it's not an issue with contract itself, e.g. `IList`. Someone could just as well implement `IReadOnlyList` and make every method throw an exception too. It's kind of the opposite of duck-typing -- you've call it a duck, but it can't quack like one. That's part of the contract though, even if the compiler can't enforce it. I do 100% agree that `IReadOnlyList` or `IReadOnlyCollection`should be used for readonly-access, though. – Shelby Oldfield Nov 27 '19 at 18:28
  • 1
    @Shelby Oldfield I agree passing an immutable collection as a IList is an obvious mistake since .NET 4.6 (and it will likely be caught by the compiler). But there can be more insidious cases, such as passing a C# array as a IList. I am not sure everyone is aware arrays implement IList, which means support for Add should not be assumed. – ZunTzu Nov 28 '19 at 10:59
  • @ZunTzu I would say it's still on the caller/whoever for choosing to pass an Array in for an IList, but I wholeheartedly agree that it is ridiculous that Array 'implements' IList in the first place...you can technically check IList.IsFixedSize (which I didn't know was a thing until a minute ago. Also, it's not on IList for some reason?), so arguably it doesn't break IList's contract, but...that's still kind of bs. I really have no clue why there isn't an IReadOnlyList, IFixedSizeList, and IList, with each implementing the previous one...I'm kind of baffled that it works the way it does. – Shelby Oldfield Dec 11 '19 at 00:25
126

Some people say "always use IList<T> instead of List<T>".
They want you to change your method signatures from void Foo(List<T> input) to void Foo(IList<T> input).

These people are wrong.

It's more nuanced than that. If you are returning an IList<T> as part of the public interface to your library, you leave yourself interesting options to perhaps make a custom list in the future. You may not ever need that option, but it's an argument. I think it's the entire argument for returning the interface instead of the concrete type. It's worth mentioning, but in this case it has a serious flaw.

As a minor counterargument, you may find every single caller needs a List<T> anyway, and the calling code is littered with .ToList()

But far more importantly, if you are accepting an IList as a parameter you'd better be careful, because IList<T> and List<T> do not behave the same way. Despite the similarity in name, and despite sharing an interface they do not expose the same contract.

Suppose you have this method:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

A helpful colleague "refactors" the method to accept IList<int>.

Your code is now broken, because int[] implements IList<int>, but is of fixed size. The contract for ICollection<T> (the base of IList<T>) requires the code that uses it to check the IsReadOnly flag before attempting to add or remove items from the collection. The contract for List<T> does not.

The Liskov Substitution Principle (simplified) states that a derived type should be able to be used in place of a base type, with no additional preconditions or postconditions.

This feels like it breaks the Liskov substitution principle.

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

But it doesn't. The answer to this is that the example used IList<T>/ICollection<T> wrong. If you use an ICollection<T> you need to check the IsReadOnly flag.

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

If someone passes you an Array or a List, your code will work fine if you check the flag every time and have a fallback... But really; who does that? Don't you know in advance if your method needs a list that can take additional members; don't you specify that in the method signature? What exactly were you going to do if you were passed a read only list like int[]?

You can substitute a List<T> into code that uses IList<T>/ICollection<T> correctly. You cannot guarantee that you can substitute an IList<T>/ICollection<T> into code that uses List<T>.

There's an appeal to the Single Responsibility Principle / Interface Segregation Principle in a lot of the arguments to use abstractions instead of concrete types - depend on the narrowest possible interface. In most cases, if you are using a List<T> and you think you could use a narrower interface instead - why not IEnumerable<T>? This is often a better fit if you don't need to add items. If you need to add to the collection, use the concrete type, List<T>.

For me IList<T> (and ICollection<T>) is the worst part of the .NET framework. IsReadOnly violates the principle of least surprise. A class, such as Array, which never allows adding, inserting or removing items should not implement an interface with Add, Insert and Remove methods. (see also https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties)

Is IList<T> a good fit for your organisation? If a colleague asks you to change a method signature to use IList<T> instead of List<T>, ask them how they'd add an element to an IList<T>. If they don't know about IsReadOnly (and most people don't), then don't use IList<T>. Ever.


Note that the IsReadOnly flag comes from ICollection<T>, and indicates whether items can be added or removed from the collection; but just to really confuse things, it does not indicate whether they can be replaced, which in the case of Arrays (which return IsReadOnlys == true) can be.

For more on IsReadOnly, see msdn definition of ICollection<T>.IsReadOnly

Kols
  • 3,641
  • 2
  • 34
  • 42
perfectionist
  • 4,256
  • 1
  • 23
  • 34
  • 10
    Great, great answer. I'd like to add that, even if `IsReadOnly` is `true` for an `IList`, you can still modify its current members! Perhaps that property should be named `IsFixedSize`? – xofz May 25 '16 at 18:55
  • (that was tested with passing an `Array` as an `IList`, so that's why I could modify the current members) – xofz May 25 '16 at 23:09
  • 1
    @SamPearson there was an IsFixedSize property on IList in .NET 1, not included in .NET 2.0's generic IList where it was consolidated with IsReadOnly, leading to surprising behaviour around Arrays. There's a detailed brain-melting blog about the subtle differences here: http://enterprisecraftsmanship.com/2014/11/22/read-only-collections-and-lsp/ – perfectionist May 26 '16 at 14:22
  • 12
    Excellent answer! Also, since .NET 4.6, we now have `IReadOnlyCollection` and `IReadOnlyList` which are almost always better fits than `IList` but don't have the dangerous lazy semantics of `IEnumerable`. And they are covariant. Avoid `IList`! – ZunTzu Nov 10 '17 at 13:29
38

List<T> is a specific implementation of IList<T>, which is a container that can be addressed the same way as a linear array T[] using an integer index. When you specify IList<T> as the type of the method's argument, you only specify that you need certain capabilities of the container.

For example, the interface specification does not enforce a specific data structure to be used. The implementation of List<T> happens to the same performance for accessing, deleting and adding elements as a linear array. However, you could imagine an implementation that is backed by a linked list instead, for which adding elements to the end is cheaper (constant-time) but random-access much more expensive. (Note that the .NET LinkedList<T> does not implement IList<T>.)

This example also tells you that there may be situations when you need to specify the implementation, not the interface, in the argument list: In this example, whenever you require a particular access performance characteristic. This is usually guaranteed for a specific implementation of a container (List<T> documentation: "It implements the IList<T> generic interface using an array whose size is dynamically increased as required.").

Additionally, you might want to consider exposing the least functionality you need. For example. if you don't need to change the content of the list, you should probably consider using IEnumerable<T>, which IList<T> extends.

casperOne
  • 73,706
  • 19
  • 184
  • 253
ILoveFortran
  • 3,441
  • 1
  • 21
  • 19
  • Regarding your last statement about using IEnumerable instead of IList. This is not always recommended, take WPF for example which will just create a wrapper IList object so will have an impact of perf - http://msdn.microsoft.com/en-us/library/bb613546.aspx – HAdes Sep 11 '09 at 10:26
  • 1
    Great answer, performance guarantees are something that an interface cannot deliver. In some code this can be quite important and using concrete classes communicates your intent, your need for that specific class. An interface on the other hand says "I just need to call this set of methods, no other contract implied." – joshperry Apr 23 '10 at 19:28
  • 1
    @joshperry If performance of an interface is bothering you you're in the wrong platform in the first place. Impo, this is microoptimization. – nawfal Jun 19 '14 at 06:31
  • @nawfal I wasn't commenting on the performance pros/cons of interfaces. I was agreeing and commenting on the fact that when you choose to expose a concrete class as an argument, you are able to communicate a performance intent. Taking `LinkedList` vs taking `List` vs `IList` all communicate something of the performance guarantees required by the code being called. – joshperry Jun 22 '14 at 17:23
29

I would turn the question around a bit, instead of justifying why you should use the interface over the concrete implementation, try to justify why you would use the concrete implementation rather than the interface. If you can't justify it, use the interface.

Patrik Hägne
  • 16,751
  • 5
  • 52
  • 60
  • 1
    Very interesting way of thinking about it. And a good way to think when programming - thanks. – Peanut Sep 09 '09 at 07:39
  • Well said! Since the interface is the more flexible approach, you really should have to justify what the concrete implementation is buying you. – Cory House Jul 23 '10 at 15:15
  • 9
    Great way of thinking. Still I can answer it: my reason is called AddRange() – PPC Nov 16 '12 at 15:22
  • 5
    my reason is called Add(). sometimes you want a list that you *know* can take additional elements, surely? See my answer. – perfectionist Apr 28 '16 at 10:57
20

IList<T> is an interface so you can inherit another class and still implement IList<T> while inheriting List<T> prevents you to do so.

For example if there is a class A and your class B inherits it then you can't use List<T>

class A : B, IList<T> { ... }
Diadistis
  • 12,086
  • 1
  • 33
  • 55
16
public void Foo(IList<Bar> list)
{
     // Do Something with the list here.
}

In this case you could pass in any class which implements the IList<Bar> interface. If you used List<Bar> instead, only a List<Bar> instance could be passed in.

The IList<Bar> way is more loosely coupled than the List<Bar> way.

smack0007
  • 11,016
  • 7
  • 41
  • 48
15

A principle of TDD and OOP generally is programming to an interface not an implementation.

In this specific case since you're essentially talking about a language construct, not a custom one it generally won't matter, but say for example that you found List didn't support something you needed. If you had used IList in the rest of the app you could extend List with your own custom class and still be able to pass that around without refactoring.

The cost to do this is minimal, why not save yourself the headache later? It's what the interface principle is all about.

annakata
  • 74,572
  • 17
  • 113
  • 180
  • 4
    If your ExtendedList inherits List, then you can still fit it in a List contract. This argument only works if you write your own implementation of IList from sratch (or at least without inheriting List) – PPC Nov 16 '12 at 15:27
14

Supprising that none of these List vs IList questions (or answers) mentions the signature difference. (Which is why I searched for this question on SO!)

So here's the methods contained by List that are not found in IList, at least as of .NET 4.5 (circa 2015)

  • AddRange
  • AsReadOnly
  • BinarySearch
  • Capacity
  • ConvertAll
  • Exists
  • Find
  • FindAll
  • FindIndex
  • FindLast
  • FindLastIndex
  • ForEach
  • GetRange
  • InsertRange
  • LastIndexOf
  • RemoveAll
  • RemoveRange
  • Reverse
  • Sort
  • ToArray
  • TrimExcess
  • TrueForAll
JasonS
  • 7,443
  • 5
  • 41
  • 61
  • 1
    Not completely true. `Reverse` and `ToArray` are extension methods for IEnumerable interface, which IList derives from. – Tarec Nov 30 '15 at 13:19
  • That's because these only make sense in `List`s and not other implementations of `IList`. – HankCa Feb 14 '18 at 02:40
14

The most important case for using interfaces over implementations is in the parameters to your API. If your API takes a List parameter, then anyone who uses it has to use List. If the parameter type is IList, then the caller has much more freedom, and can use classes you never heard about, which may not even have existed when your code was written.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
7

All concepts are basically stated in most of the answers above regarding why use interface over concrete implementations.

IList<T> defines those methods (not including extension methods)

IList<T> MSDN link

  1. Add
  2. Clear
  3. Contains
  4. CopyTo
  5. GetEnumerator
  6. IndexOf
  7. Insert
  8. Remove
  9. RemoveAt

List<T> implements those nine methods (not including extension methods), on top of that it has about 41 public methods, which weighs in your consideration of which one to use in your application.

List<T> MSDN link

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
yantaq
  • 3,968
  • 2
  • 33
  • 34
7

What if .NET 5.0 replaces System.Collections.Generic.List<T> to System.Collection.Generics.LinearList<T>. .NET always owns the name List<T> but they guarantee that IList<T> is a contract. So IMHO we (atleast I) are not supposed to use someone's name (though it is .NET in this case) and get into trouble later.

In case of using IList<T>, the caller is always guareented things to work, and the implementer is free to change the underlying collection to any alternative concrete implementation of IList

casperOne
  • 73,706
  • 19
  • 184
  • 253
Soundararajan
  • 2,000
  • 21
  • 23
4

You would because defining an IList or an ICollection would open up for other implementations of your interfaces.

You might want to have an IOrderRepository that defines a collection of orders in either a IList or ICollection. You could then have different kinds of implementations to provide a list of orders as long as they conform to "rules" defined by your IList or ICollection.

NotAgain
  • 1,927
  • 3
  • 26
  • 42
Peter Lindholm
  • 2,420
  • 5
  • 23
  • 27
3

IList<> is almost always preferable as per the other poster's advice, however note there is a bug in .NET 3.5 sp 1 when running an IList<> through more than one cycle of serialization / deserialization with the WCF DataContractSerializer.

There is now a SP to fix this bug : KB 971030

StuartLC
  • 104,537
  • 17
  • 209
  • 285
2

The interface ensures that you at least get the methods you are expecting; being aware of the definition of the interface ie. all abstract methods that are there to be implemented by any class inheriting the interface. so if some one makes a huge class of his own with several methods besides the ones he inherited from the interface for some addition functionality, and those are of no use to you, its better to use a reference to a subclass (in this case the interface) and assign the concrete class object to it.

additional advantage is that your code is safe from any changes to concrete class as you are subscribing to only few of the methods of concrete class and those are the ones that are going to be there as long as the concrete class inherits from the interface you are using. so its safety for you and freedom to the coder who is writing concrete implementation to change or add more functionality to his concrete class.

recursive
  • 83,943
  • 34
  • 151
  • 241
vishy
  • 21
  • 1
2

You can look at this argument from several angles including the one of a purely OO approach which says to program against an Interface not an implementation. With this thought, using IList follows the same principal as passing around and using Interfaces that you define from scratch. I also believe in the scalability and flexibility factors provided by an Interface in general. If a class implmenting IList<T> needs to be extended or changed, the consuming code does not have to change; it knows what the IList Interface contract adheres to. However using a concrete implementation and List<T> on a class that changes, could cause the calling code to need to be changed as well. This is because a class adhering to IList<T> guarantees a certain behavior that is not guaranteed by a concrete type using List<T>.

Also having the power to do something like modify the default implementation of List<T> on a class Implementing IList<T> for say the .Add, .Remove or any other IList method gives the developer a lot of flexibility and power, otherwise predefined by List<T>

atconway
  • 20,624
  • 30
  • 159
  • 229
0

Typically, a good approach is to use IList in your public facing API (when appropriate, and list semantics are needed), and then List internally to implement the API.  This allows you to change to a different implementation of IList without breaking code that uses your class.

The class name List may be changed in next .net framework but the interface is never going to change as interface is contract.

Note that, if your API is only going to be used in foreach loops, etc, then you might want to consider just exposing IEnumerable instead.

lazydeveloper
  • 891
  • 10
  • 20