There's lots you lose with IEnumerable<T>. Count and random access are the most obvious. LINQ sort of gives this back to you. The Count() method will give you the count, and is optimized to try and cast the IEnumerable<T> to an ICollection<T> in order to
get the count without having to iterate over the collection. However, I believe that is bad design. If the underlying collection is changed to no longer be an ICollection<T> the complexity of Count() has radically changed. Since the public interface only
exposed IEnumerable<T>, I have to expect that sort of behavior.
The ReadOnlyCollection types aren't ideal. I'd much prefer it if the collection designs had started with IReadOnlyCollection and ICollection had derived from there. IOW, I share your consternation about the existence of non-readonly methods that throw exceptions.
That said, however, we have what we have, and in practice there's seldom if ever an issue with the IsReadOnly/exception design of ReadOnlyCollection types.
Interesting that you should bring up the concept of an IReadOnlyCollection from which ICollection
would derive. This idea was really the starting point of the thought process of my original post in this thread. Basically, I asked myself what it was that I wanted IReadOnlyCollection to give me that I didn't get from IEnumerable+LINQ.
Your point about "complexity" is not without merit and is something that I had considered. On the other hand, unless you are referencing a sealed class, I don't think there are ever any guarantees about such things. To put it another way, I think it is the
responsibility of the language to help us validate the correctness of our program, but it is entirely our responsibility to determine the fitness of tool "X" for task "Y". I haven't met a language yet that doesn't make it trivially easy to do something really
Hey, maybe we can get the Spec# folks to incorporate algorithmic complexity as a contract