Comparing ICollection and ICollection<T>

When examining the ICollection<T> interface prior to implementing on my Heap class, I was quite surprised, as it had changed rather more than I'd expected from the original ICollection interface. Here they are for comparison:

   public interface ICollection<T> : System.Collections.Generic.IEnumerable<T>
      void Add(T item);
      void Clear();
      bool Contains(T item);
      void CopyTo(T[] array, int arrayIndex);
      int Count { get; }
      bool IsReadOnly { get; }
      bool Remove(T item);

   public interface ICollection : System.Collections.IEnumerable
      void CopyTo(System.Array array, int index);
      int Count { get; }
      bool IsSynchronized { get; }
      object SyncRoot { get; }

Quite a difference, I think you'll agree. CopyTo and Count remain (albeit strongly typed thanks to generics in the case of CopyTo). However, pretty much everything else changes. Firstly, the additions. Add, Remove, Clear and Contains are added as richer collection manipulation functions. I have mixed feelings about this. On the one hand, its nice to be able to rely on a richer set of functionality if you get handed an ICollection<T> reference. However, these aren't appropriate functions for all types of collection; Stacks and Heaps, for example, where the element that can be removed is controlled by the collection, not by the client. The system supplied Stack<T> class works around this by throwing an InvalidOperationException when you call Remove on it (I took the same approach in my Heap). However this means that in my client code that received an ICollection<T> reference I now have to expect exceptions on a simple operation and am back to writing special case code. As Remove (with the signature provided) is the only function that is commonly not appropriate I guess this is a reasonable compromise - just remember not to rely on that method in your generic client code.

The next addition is the IsReadOnly property. This is a really welcome addition, as it encourages collections to provide read-only variants and clients can easily modify their functionality to take account of the read-only status without incurring a relatively expensive exception throw. However, its not a panacea. I started out implementing a read-only variant of my heap in a flurry of enthusiasm, only to stop after a few minutes and think a little harder. As my heap iterator doesn't provide iteration in heaped order (merely unspecified order) then a read-only variant is of very little use. Removing items in order from the heap changes it, meaning its not an operation that is possible on a read-only instance.

Now on to the removals. IsSychronized and SyncRoot have gone. These methods were used to provide a standard way to implement a thread-safe wrapper around your collection or to allow a client to do so. Personally I liked this scheme, although there were many issues around the appropriate object to use as a SyncRoot. Of course, if your collection still implements the old ICollection as well as the new ICollection<T> then you'd get the best of both worlds. I simply left my old implementations of the functions in place as public methods and made my SychronizedHeap class generic as well.

Skip to main content