Any chance of an IReadOnlyList collection interface? [Kit George]

Any chance of an IReadOnlyList collection interface? [Kit George]

  • Comments 8

Joe White asked for this specific feature:

For the most part, I like the way you designed collections in .NET -- especially interfaces like IEnumerable, ICollection, and IList.  The problem is, you jump straight from a read-only, non-random-access collection (ICollection) to a read/write, random-access collection (IList).  Very often, it would be useful to have something in between -- a read-only, but still random-access, list, with a read-only indexer but without some of the methods like Add, Clear, Remove, etc.  Ideally IReadOnlyList would descend from ICollection, and IList would then descend from IReadOnlyList.  Any chance something like this could make it into a future version of the Framework?

Because we could provide a default implementation for what you're talking about Joe, rather than giving you an interface, we provided ReadOnlyCollectionBase. However, given that it wasn't strongly typed, I can understand a reluctance to use it. But with the introduction of generics, we now also have ReadOnlyCollection<T>, so you get the same functionality, but strongly-typed: awesome!

ReadOnlyCollection<T> isn't sealed, so feel free to write your own collection on top if needed. We have no plans to introduce an interface for this same concept, since the collections we've made for this suit the general need.

  • This is a newbie question but I'm confused. Why isn't the ReadOnlyCollection strongly typed? I have a class that inherits from ReadOnlyCollection and I can use it w/o doing lots of unecessary casting.
  • *sigh*

    So close, yet so far. Why not give us an interface *and* a default implementation? If I decide that it'd be better to define a certain property as an interface and not a class, I'm not going to change my mind just because the appropriate interface hasn't been included in the BCL. In this case, I'll just end up defining an IReadOnlyList equivalent myself, and that means more code and more documentation that I have to write, and one more type that the guy using my library is going to have to wrap his brain around.

    The more I learn about the new collections, the more I realize that they really aren't going to do much for me. For example, most of the work I do with collections involves type-safe, modifiable types that do not have list semantics. That is, all I'm really looking for are Add(T), Contains(T), Clear, and Remove(T) methods on top of the ICollection<T> interface. Again, given a choice between defining my own types or making the jump to IList (with its extraneous members and list semantics), I'm going to choose the first choice every time. I understand that you need to think long and hard before adding a new type to the framework, but it seems like this is the kind of stuff you should be throwing at us by the handful.
  • More on my IReadOnlyList suggestion:

    I'm not looking for an *implementation* of a read-only list; I already use ReadOnlyCollectionBase extensively. I'm looking for an *interface* for read-only list access (whether the underlying list is read-only or not; it would be useful both ways).

    Let's say I write a method that sums all the numbers in a collection. (This is a trivial example; yes, IEnumerable would work perfectly well for this, but pretend that I really need the random access.) Sum() won't modify the contents of that collection (or shouldn't, anyway!), and I want to make that fact clear in the code. So I want to be able to make its method signature look like this:

    public static int Sum(IReadOnlyList<int> values);

    This would have several advantages:
    * IReadOnlyList<> doesn't have a setter for its indexer, and doesn't have Add(), Clear(), Insert(), Remove(), or RemoveAt() methods. So it would be a compile-time error if Sum *did* try to modify the collection.
    * Therefore, the above signature is intention-revealing. It's immediately clear to the caller that Sum() cannot modify the collection.
    * I can pass a List<int>, an int[], or an IntCollection (descended from CollectionBase) to this method.
    * I can also pass a ReadOnlyCollection<int> or a ReadOnlyIntCollection (descended from ReadOnlyCollectionBase).

    Currently, I can't achieve all of the above goals. If I need random access, my parameter type has to be either IList or a specific class. If I use ICollection, my method can't do random access into the collection. If I use IList, I lose the compile-time enforcement of read-only access, and I also lose the ability to pass a ReadOnlyCollectionBase for that parameter. (Sure, I can pass an IList with IsReadOnly = true, e.g. by calling ArrayList.ReadOnly(IList), but I'd much rather catch the bug at compile time.) And if I use ReadOnlyCollection<int> as my parameter type, I lose the ability to call that method with int[], ICollection<int>, and even List<int>.

    Does that help clarify where I'm coming from?
  • Joe, your idea is a great one, very similar to the concepts in STL. I too would like to see a logical, professional set of interfaces, like the STL. Many times I have needed a read-only specification exactly as you.
  • David, we change the ICollection<T> in Whidbey. It will include Add, Remove, Contains and a few other methods. This will make ICollection<T> useful.

    Joe, you will able to create a ReadOnlyCollection<T> easily in Whidbey. There will be some methods on Array and List<T> to return you a readonly adapter. In addition, Array will be implementing IList<T> in whidbey.
  • It may sound surprising, or not, but IList and IList<T> are our interfaces intended for read-only collections. They both have IsReadOnly Boolean property that should return true when implemented by a read-only collection. The reason we don’t want to add a purely read-only interface is that we feel it would add too much unnecessary complexity to the library. Note that by complexity, we mean both the new interface and its consumers.

    We feel that API designers either don’t care about checking the IsReadOnly property at runtime and potentially throwing an exception, in which case IList is fine, or they would like to provide a really clean custom API, in which case they explicitly implement IList and publicly expose custom tailored read-only API. The latter is typical for collections exposed form object models.

    Of course there are scenarios where IReadOnlyList<T> would be better. We just think it would be not that much better and there are not that many of these scenarios.

    That said, your input is very valuable as we have not ruled out adding such interface in the future. We just want to be very very careful. We are listening to the feedback and trying to weigh all the pros and cons.

    Thanks.
  • Gang Peng: Um... if ICollection<T> will have methods for Add, Remove, etc., then how will it be different from IList<T>? And why on earth was this change made? Are you only changing the generic ICollection (meaning you want to fry our brains by making us remember which ICollection we're dealing with), or are you changing the non-generic ICollection as well (meaning massive-scale breaking changes)? Will the generic version of ReadOnlyCollectionBase implement the new, overweight ICollection<T> (and if so, then why didn't it implement IList in the first place?), or will it only implement IEnumerable? Are you moving Count and CopyTo() up into IEnumerable to fill the need for non-writable collections? (I'm joking on that last one. I hope.) I'm willing to be persuaded that this change is a good thing, but...


    Krzysztof: I understand that the status quo is to use IList for even read-only lists, but I'm not crazy about that solution, because the compiler can't type-check it for me -- and after all, type safety is supposed to be one of the selling points of .NET. Yes, I know you can't make a different interface for every possible subset of IList functionality; but read-only collections are tremendously useful, and increasingly common under FxCop's influence (no! don't make a property that returns an array! bad developer!) There's a lot of value in having such a thing right there in the BCL.

    Quick example: Imagine if DirectoryInfo.GetFiles() returned an IReadOnlyList<FileInfo> instead of a FileInfo[] array. Now there are new possibilities for optimization: it can cache the results between calls, or do lazy reads, or fill the list asynchronously from a background thread. Or it could just build a new array each time, like it does now; the point is that the method signature no longer needs to drive the implementation. Internally, it can build and return an array, a generic list, a ReadOnlyCollectionBase, or a totally custom class, without casting and without wrappers. The compiler tells you if a caller screws up and tries to write to the list, meaning less time spent debugging mysterious exceptions -- especially if you're passing the collection to third-party code. Nothing currently available can do all that.

    And yes, I could write my own IReadOnlyList interface, but as I already pointed out, arrays wouldn't implement it, and neither would Framework classes like ArrayList and List<T>, and neither would third parties. For it to work, it really needs to be an integral part of the BCL. That's why I want you guys to write it for me. ;-)
  • PingBack from http://greenteafatburner.info/story.php?id=4726

Page 1 of 1 (8 items)