7

I have always been taught that programming against an interface is better, so parameters on my methods I would set to IList<T> rather than List<T>..

But this means I have to cast to List<T> just to use some methods, one comes to mind is Find for example.

Why is this? Should I continue to program against interfaces, but continue to cast or revert?

I am a little bit confused why Find (for example) isn't available on the IList<T> which List<T> inherits from.

code4life
  • 15,655
  • 7
  • 50
  • 82
Martin
  • 23,844
  • 55
  • 201
  • 327
  • 3
    I suspect that the choice of terminology in your last sentence underscores your confusion. A class can't *inherit* an interface. Rather, it *implements* an interface. If the interface doesn't require it to implement a particular function (say `Find`), then it doesn't have to do so. It's not the same as an inheritance relationship, where a derived class would inherit any functions defined on the base class. – Cody Gray - on strike Mar 04 '11 at 13:29
  • possible duplicate of [C# - List or IList](http://stackoverflow.com/questions/400135/c-sharp-listt-or-ilistt) – nawfal Jun 19 '14 at 06:39

8 Answers8

12

Personally I would use IList<T> rather than List<T>, but then use LINQ (Select, Where etc) instead of the List-specific methods.

Casting to List<T> removes much of the point of using IList<T> in the first place - and actually makes it more dangerous, as the implementation may be something other than List<T> at execution time.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • a very good point that I hadn't seen brought up before. allows you to have your interface and eat it too. – dove Mar 04 '11 at 13:34
  • thanks Jon!.. really good explanation and the perfect solution. Thanks! – Martin Mar 06 '11 at 13:01
6

In the case of lists you could continue programming against interfaces and use LINQ to filter your objects. You could even work with IEnumerable<T> which is even higher in the object hierarchy.

But more generally if the consumer of your API needs to call a specific method you probably haven't chosen the proper interface to expose.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
3

I am a little bit confused why Find (for example) isn't available on the IList which List inherits from.

While I'm not privy to the decision process of the designers, there are a few things they were probably thinking.

1) Not putting these methods on IList keeps the intent of the contract clearer. According to MSDN, IList "Represents a collection of objects that can be individually accessed by index." Adding Find would change the contract to a searchable, indexable collection.

2) Every method you put on an interface makes it harder to implement the interface. If all of those methods were on IList, it would be much more tedious to implement IList. Especially since:

3) Most implementations of these methods would be the same. Find and several of the others on List would really be better placed on a helper class. Take for example, ReadOnlyCollection, Collection, ObservableCollection, and ReadOnlyObservableCollection. If I had to implement Find on all of those (pre-LINQ), I would make a helper class that takes IEnumerable and a predicate and just loop over the collections and have the implementations call the helper method.

4) LINQ (Not so much a reason why it didn't happen, more of why it isn't needed in the future.) With LINQ and extension methods, all IEnumerable's now "have" Find as an extension method (only they called it Where).

Gideon Engelberth
  • 6,095
  • 1
  • 21
  • 22
1

I think it's because IList can be different collection types (ie. an IEnumerable of some sort, an array or so).

You can use the Where extension method from System.Linq. Avoid casting back to List from IList.

lasseeskildsen
  • 599
  • 6
  • 15
1

If you find that the IList<T> parameter being passed between various classes is consistently being recast into List<T>, this indicates that there is a fundamental problem with your design.

From what you're describing, it's clear that you want to use polymorphism, but recasting on a consistent basis to List<T> would mean that IList<T> does not have the level of polymorphism you need.

On the other side of the coin, you simply might be targeting the wrong polymorphic method (e.g., Find rather than FirstOrDefault).

In either case, you should review your design and see what exactly you want to accomplish, and make the choice of List<T> or IList<T> based on the actual requirements, rather than conformity to style.

code4life
  • 15,655
  • 7
  • 50
  • 82
1

If you expose your method with a IList<> parameter, someone can pass, for exemple, a ReadOnlyCollection<>, witch is an IList<> but is not a List<>. So your API will crash at runtime. If you expose a public method with a IList<> parameter, you cannot assume that it is a specific implementation of an IList<>. You must use it as an IList<> an nothing more.

Johnny5
  • 6,664
  • 3
  • 45
  • 78
  • Agreed. I would also say, but when returning a value, the opposite is true. You should always return the richest version of the object you have. So if your methods builds a List, don't downgrade it to an IList or an IEnumerable. There's no advantage to doing it. The caller can use is it as a List, IList or IEnumerable if they want. Moreover, if you do downgrade it, and the caller needs a richer version, they'd be forced to call ToList(), which could have been avoided. The only exception to this rule is if the design an interface favors using the more generic type. – zumalifeguard Apr 21 '18 at 16:55
0

If the list is some part of an Api or service that is exposed then it is probably better to have as an IList to allow the change of the implementation internally.

There is already much discussion on this topic.

Community
  • 1
  • 1
dove
  • 20,469
  • 14
  • 82
  • 108
0

No, in this case it has no sense to program to interfaces, because your List is NOT an IList, having extra methods on it.

Victor Ionescu
  • 1,967
  • 2
  • 21
  • 24
  • So if I make a class that implements two interfaces with different functionality, that class is not either of those interfaces? (Yet it's perfectly legal to cast it to either interface.) What kind of reasoning is that? – user Mar 04 '11 at 13:42
  • 1
    When you call find on it, it's not an IList, isn't it? The idea is that you program to interfaces if you want to vary the implementation, in this case the implementation is imposed (hence the need to cast back to List), in a happy world we would have a subinterface of IList implementing the find() method, but since we don't have that, the interface IList just doesn't fit. – Victor Ionescu Mar 04 '11 at 13:54
  • If you treat it as an IList, you will never call Find on it in the first place, since that method is not defined by the IList interface and thus it won't compile. (As a better example than in my first comment, you can have an interface that inherits from two other interfaces as well, combining them, and a class that implements the composite interface; you can use that class type anywhere any of the interfaces are expected, and if you know for a fact what you are doing, you can treat it as a more specialized interface or implementation.) – user Mar 04 '11 at 13:59
  • That, however, does not mean that a List "is not" an IList. Any class that implements IList "is" an IList - and possibly many other things as well, depending on what else it implements. – user Mar 04 '11 at 14:00
  • Yes, you are right, add a proper interface into the mix, but not IList. – Victor Ionescu Mar 04 '11 at 14:02