Question

Following the rules that a public APIs should never return a list, i'm blinding converting all code that returned lists, to return ICollection<T> instead:

public IList<T> CommaSeparate(String value) {...}

becomes

public ICollection<T> CommaSeparate(String value) {...}

And although an ICollection has a Count, there is no way to get items by that index.

And although an ICollection exposes an enumerator (allowing foreach), i see no guarantee that the order of enumeration starts at the "top" of the list, as opposed to the "bottom".

i could mitigate this by avoiding the use of ICollection, and instead use Collection:

public Collection<T> Commaseparate(String value) {...}

This allows the use of an Items[index] syntax.

Unfortunately, my internal implementation constructs an array; which i can be cast to return IList or ICollection, but not as a Collection.

Is there a ways to access items of a collection in order?

This begs the wider question: Does an ICollection even have an order?


Conceptually, imagine i want to parse a command line string. It is critical that the order of items be maintained.

Conceptually, i require a contract that indicates an "ordered" set of string tuples. In the case of an API contract, to indicate order, which of the following is correct:

IEnumerable<String> Grob(string s)

ICollection<String> Grob(string s)

IList<String> Grob(string s)

Collection<String> Grob(string s)

List<String> Grob(string s)
Was it helpful?

Solution

The ICollection<T> interface doesn't specify anything about an order. The objects will be in the order specified by the object returned. For example, if you return the Values collection of a SortedDictionary, the objects will be in the the order defined by the dictionary's comparer.

If you need the method to return, by contract, a type whose contract requires a certain order, then you should express that in the method's signature by returning the more specific type.

Regardless of the runtime type of the object returned, consider the behavior when the static reference is IList<T> or ICollection<T>: When you call GetEnumerator() (perhaps implicitly in a foreach loop), you're going to call the same method and get the same object regardless of the static type of the reference. It will therefore behave the same way regardless of the CommaSeparate() method's return type.

Additional thought:

As someone else pointed out, the FXCop rule warns against using List<T>, not IList<T>; the question you linked to is asking why FXCop doesn't recommend using IList<T> in place of List<T>, which is another matter. If I imagine that you are parsing a command-line string where order is important, I would stick with IList<T> if I were you.

OTHER TIPS

ICollection does not have a guaranteed order, but the class that actually implements it may (or may not).

If you want to return an ordered collection, then return an IList<T> and don't get too hung up on FxCop's generally sound, but very generic, advice.

No, ICollection does not imply an order.

The ICollection instance has the "order" of whatever class that implements it. That is, referencing a List<T> as an ICollection will not alter its order at all.

Likewise, if you access an unordered collection as an ICollection, it will not impose an order on the unordered collection either.

So, to your question:

Does an ICollection even have an order?

The answer is: it depends solely on the class that implements it.

ICollection<T> may have an order, but the actual ordering depends on the class implementing it. It does not have accesor for an item at given index. IList<T> specializes this interface to provide access by index.

An ICollection<T> is just an interface; whether it's ordered or not is entirely dependent up the implementation underlying it (which is supposed to be opaque).

If you want to be able to access it by index, you'd want to return things as an IList<T>, which is both IEnumerable<T> and ICollection<T>. One should bear in mind, though, that depending on the underlying implementation, that getting at an arbitrary item in the collection could require O(N/2) time on the average.

My inclination would be to avoid the 'collection' interfaces altogether and instead use a custom type representing the collection in terms of the problem domain and exposing the appropriate logical operations suitable for that type.

ICollection is just an interface—there is no implementation or explicit specification about ordering. That means if you return something that enumerates in an ordered manner, whatever is consuming your ICollection will do so in an ordered manner.

Order is only implied by the underlying, implementing, object. There is no specification in ICollection that says it should be ordered or not. Enumerating over a result multiple times will invoke the underlying object's enumerator, which is the only place that those rules would be set. An object doesn't change the way it is enumerated just because it inherits this interface. Had the interface specified that it is an ordered result, then you could safely rely on the ordered result of the implementing object.

It depends on the implementation of the instance. An ICollection that happens to be a List has an order, an ICollection that happens to be a Collection does not.

All ICollections implement IEnumerable, which returns the items one at a time, ordered or otherwise.

EDIT: In reply to your additional example about command line parsing in the question, I would argue that the appropriate return type depends on what you are doing with those arguments afterward, but IEnumerable is probably the right one.

My reasoning is that IList, ICollection, and their concrete implementations permit modification of the list returned from Grob, which you probably don't want. Since .NET doesn't have an Indexed Sequence interface, IEnumerable is the best bet to prevent your callers from doing something weird like trying to modify the parameter list that they get back.

If you expect that all present and future versions of your method will have no difficulty returning an object that will be able to quickly and easily retrieve the Nth item, use type IList<T> to return a reference to something that implements both IList<T> and non-generic ICollection. If you expect that some present or future versions might not be able to quickly and easily return the Nth item, but would be able to instantly report the number of items, use type ICollection<T> to return a reference something that implements ICollection<T> and non-generic ICollection. If you expect that present or future versions may have trouble even knowing how many items there are, return IEnumerable<T>. The question of sequencing is irrelevant; the ability to access the Nth thing implies that a defined sequence exists, but ICollection<T> says neither more nor less about sequencing than IEnumerable<T>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top