When IReadOnlyList<T>
was introduced in .NET 4.5, for a moment I thought the missing part of the puzzle was finally inserted in place: a way to pass a true readonly indexable interface where previously I would have to use my own read-only interfaces and create wrapper classes around everything.
I was expecting the interface to be placed inside the "natural" hierarchy, which would ideally be:
IEnumerable<T>
.GetEnumerator()
-> IReadOnlyCollection<T> : IEnumerable<T>
.Count
-> IReadOnlyList<T> : IReadOnlyCollection<T>
.Item[...]
-> IList<T> : IReadOnlyList<T>
.Add(...)
.Clear()
.Contains(...)
(etc)
But, as it turns out, IList<T>
doesn't inherit from IReadOnlyList<T>
.
Is there a reason for this?
Some clarification:
Note that IReadOnlyList<T>
is merely a contract which states that the list provides a way to get the list count and read the value at a certain index. It's poorly-named, because it doesn't enforce that the actual implementation is readonly.
A List<T>
implements IEnumerable<T>
, an IList<T>
inherits from IEnumerable<T>
, but this doesn't mean that these classes can only be enumerated.
So, if you want to pass a list to a method, and only allow it to be indexed (read), but not modified, you need to wrap it in a new instance. At the same time, you can pass it to a method which accepts IEnumerable<T>
or IList<T>
without having to wrap it. This is what I find to be broken.
I also believe the proper name should had been something like ICountable
for IReadOnlyCollection
and IIndexable
for the IReadOnlyList
:
IEnumerable<T>
.GetEnumerator()
-> ICountable<T> : IEnumerable<T>
.Count
-> IIndexable<T> : ICountable<T>
.Item[...]
-> IList<T> : IIndexable<T>
.Add(...)
.Clear()
.Contains(...)
(etc)
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…