wkempf said:
TommyCarlier said:
*snip*
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.
Be aware that the reason Count and Random access are not provided in IEnumerable<> is quite deliberate - whereas random access and counting in a List<T> is fast, random access and counting in a LinkedList<> is not - furthermore, IEnumerable<T> allows some interesting properties - for example there's no reason why an IEnumerable<T> should actually ever terminate, and it's certainly not nessisarilly the case that it should know if (or when) it's going to terminate when it starts.

For example, if you're reading through a series of lines in a text file, there's a good argument to say that you only need to read them one at a time. Finding out how many lines there are requires you to read the entire file into memory (and the file may be big), but reading each line into memory one at a time does not require you to read all of the file into memory. Thus by using IEnumerable<T> rather than a Collection<T>, some benefit has been achieved.

Another example is when tokenising a HTML stream to be rendered - if we were to insist on knowing how many elements there were before we started, then we would need to start parsing after the entire page contents has returned. If on the other hand we do not need to do this (by using an IEnumerable<T> rather than an ICollection<T>) we can start processing the page BEFORE the entire thing has been downloaded - allowing for a speedup of the effective render time for the page.

One of the things that slightly concerns me with some of the comments on this page is people noting that if you have a List<T> and you expose this as an IEnumerable<T> that this can (in principle) be cast back to a List<T> and modified - but as soon as I see this I think "that sentiment is certainly true - but if a programmer is willing to forego safe programming practises, is that not his/her fault when it all goes wrong?". Certainly in my code I expect that when I expose an IEnumerable<T> that either myself or any other programmer will never try to cast it back to a List<T> - if they need random access they can use the constructor of List<T> that takes an IEnumerable<T>, but according to the contract that the method gives, it does not say that the IEnumerable<T> is castable to a List<T>, and to assume that it can be cast back is an unsafe assumption - if later I change the implementation to a LinkedList<T> then their code will break, even though the method signature will remain the same.

The question you need to then ask is WHY and WHO you are doing these optimisations for. If you're a component maker who's exposing these properties to a customer, then sure, go right ahead and protect these things with ReadOnlyCollections. If on the other hand you're programming to prevent yourself or your collegages from doing dodgy assumptions, then perhaps you and your collegagues need to have a sit down and discuss whether there is a better way of doing what they're doing.

Remember:

class {
 List<T> _val;
  public IEnumerable<T> {
  get { return _val; }
 }
}

has a computational complexity of 1 and memory overhead of 0.

class {
  List<T> _val;
  public IEnumerable<T> {
    get { return new ReadOnlyCollection(_val); }
  }
}

has a computational complexity and memory overhead of O(_val.Count) - and so you're slowing and bloating your program at the same time.