48

Why does List<T> implement IReadOnlyList<T> in .NET 4.5?

List<T> isn't read only...

James Newton-King
  • 48,174
  • 24
  • 109
  • 130

7 Answers7

52

Because List<T> implements all of the necessary methods/properties/etc. (and then some) of IReadOnlyList<T>. An interface is a contract that says "I can do at least these things."

The documentation for IReadOnlyList<T> says it represents a read-only collection of elements.

That's right. There are no mutator methods in that interface. That's what read-only means, right? IReadOnlyList<T> is used in the "typical" (contract) way, not as a marker.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • 1
    So? The documentation for IReadOnlyList says it represents a read-only collection of elements. List isn't read only so why does it implement the interface? http://msdn.microsoft.com/en-us/library/hh192385.aspx – James Newton-King Mar 07 '13 at 04:20
  • 39
    The interface doesn't guarantee that the list cannot be modified. It just means you can't modify it through the interface. – Brandon Mar 07 '13 at 04:22
  • 1
    @JamesNewton-King It's all semantics. From interface inheritance point of view a read-only functionality is a subset of read-write functionality. So here you go. – Andrew Savinykh Mar 07 '13 at 04:22
  • 3
    Here's an interesting read on the IReadOnly interfaces... http://social.msdn.microsoft.com/Forums/en/netfxbcl/thread/b4fb991a-3f5c-4923-93d4-7cd5c004f859 – Brandon Mar 07 '13 at 04:24
  • 17
    Ok by this logic then shouldn't IList implement IReadOnlyList? I think it is silly. – James Newton-King Mar 07 '13 at 04:26
  • 10
    It should! But it can't because it would be a breaking change. Read the link I provided earlier. – Brandon Mar 07 '13 at 04:28
  • 2
    @JamesNewton-King sure, in an ideal world, where legacy code and backwards compatibility aren't concerns, that would make sense. But in the real world, it would be a breaking change. [You're not the first person to think of it.](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2845892-make-ilist-t-inherited-from-ireadonlylist-t-) Good API design is hard. Perfect API design is impossible. Getting it right the first time is even more impossible. – Matt Ball Mar 07 '13 at 04:28
  • 5
    The "is a" interpretation of a type and subtype relationship is useful for starting out with OO programming. However, it doesn't hold true very far. – Tormod Mar 07 '13 at 04:38
  • 9
    *"That's what read-only means, right?"* -- Well, no. I'm not a lawyer, but I'm fairly certain that a *contract* which reads "*only*" is very different than one that reads "*at least*". The term is correctly applied in the `IsReadOnly` property of `IList`, which is `false` when reading *and* writing are possible. The interface should be named something like `IIndexable`. – nmclean Feb 04 '14 at 15:18
  • 2
    @JamesNewton-King: It would have been useful for .NET to include an `IImmutableList` which would only be implemented by immutable collections [not necessarily array-backed ones; the return from `IEnumerable.Range` could implement `IImmutableList` by having the indexer compute on demand the value of the nth element. – supercat Jun 19 '14 at 02:05
  • 2
    @MattBall Yeah, that makes sense, thanks. Would've been nice if it's name worked. Perhaps IReadableList instead of IReadOnly – imma Sep 23 '15 at 08:24
31

Interfaces only describes functionality which will be implemented. It does not describe functionality which will not be implemented. IReadOnlyList is therefore an incorrect interface name, as it cannot dictate that write functionality will not be written.

The methods/functions of describe that you can read the contents of the list. The interface should have been IReadableList instead of IReadOnlyList.

Pang
  • 9,564
  • 146
  • 81
  • 122
Rbrt
  • 311
  • 3
  • 2
6

Implementing an interface isn't the same as "marking" it. List<T> also implements IEnumerable<T>, but that doesn't mean you're limited to simply enumerating it.

They added the read-only interfaces for API creation, not so you could mark your read-only types with an interface. It allows me to use IReadOnlyCollection<T> for arguments when I just want to know the number of elements in the collection without enumerating it, or IReadOnlyList<T> when I need to reference the elements in the collection by their index. This is good for everyone-- I can be specific about what I need from my caller while allowing my caller to use whatever collection type he wants, as long as it meets the minimum standard I set through the argument type.

So I think the more difficult question is, why wouldn't you have List<T> implement IReadOnlyList<T>?

David Schwartz
  • 1,956
  • 19
  • 28
  • A similar "why _wouldn't_ you" question is asked at https://stackoverflow.com/questions/12622539/why-doesnt-generic-icollection-implement-ireadonlycollection-in-net-4-5. The answer comes down to "doing so would have caused backcompat breakages." It makes me sad. – Tim Sparkles Aug 29 '19 at 18:26
5

The fact that it implements the interface doesn't mean it is read-only. But because it implements the interface you can now pass it to methods that expect an IReadOnlyList<T>. So the way to look at it, is that it implements the read-only list interface...along with some write methods.

Brandon
  • 38,310
  • 8
  • 82
  • 87
3

The interfaces IReadOnlyList and IReadOnlyCollection are somewhat confusing because they do not mean that the collection is read-only, just that read-only access is supported. From the MSDN documentation (scroll down to Remarks)

The content of list elements is not guaranteed to be read-only.

A better name would be IReadable, see Why doesn't generic ICollection implement IReadOnlyCollection in .NET 4.5?. Furthermore, this implies that IList should inherit IReadOnlyList albeit it does not due to backwards compatibility, see Why doesn't IList<T> inherit from IReadOnlyList<T>?.

Community
  • 1
  • 1
Markus Hartmair
  • 672
  • 7
  • 15
1

IReadOnlyList is a substitute for the concept of "reference immutability", found in C++ but not in C#. The C++ equivalent is:

void func(T const* t) {...}

or exactly equivalent, as some prefer:

void func(const T* t) {...}

It says that the function func does not mutate the object(s) that its argument t references, called the "referent" of t. It says nothing about whether any other code mutates the referent of t, or even can.

So the C# interface is a substitute for a compiler construct. Why C# does not have the concept of reference immutability is an historical question: I think it was a mistake, but it is too late to fix it now. I think providing the interface substitute is good. I have been using an interface exactly the same as IReadOnlyList<> for years, fortunately with another name, IConstList<>. I may replace my use of IConstList<> with IReadOnlyList<>.

Kafka
  • 101
  • 2
0

Isn't it true that the code running IReadOnlyList(Of T) calls to an object that implements that interface is usually running the same thread of execution. That prevents the object from being altered by itself, unless its running on a different thread of execution, but for that situation we have synchronization calls to solve that.

Timmy
  • 1