MPavlak
MPavlak

Reputation: 2221

Guidelines for returning Generic Interface Collections

I typically try to follow a methodology of having arguments to methods be as generic as possible and return types as generic as necessary and have run into a problem to which the answer is not overly clear to me.

I have a control which operates on a structure like:

IDictionary<string, IDictionary<string, IEnumerable<MyObject>>>

So that supplying two keys, we get a collection of objects back. Pretty straight forward.

I have a class

ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>

which is just that, a readonly dictionary so that any IDictionary methods which would alter the dictionary throw an NotSupportedException();

The dictionary on which the control operates comes out of a MyObjectRepository which returns a

ReadOnlyDictionary<string, ReadOnlyDictionary<string, ReadOnlyCollection<MyObject>>>

So the question is, should I update the return type on my repository to return

IDictionary<string, IDictionary<string, IEnumerable<MyObject>>>

or ...? I don't think it would be correct to update the method to take a readonly dictionary as that is unnecessarily limiting.

It seems that this makes it less clear about the return type. Also, the interface defines the ReadOnlyDictionary as the return type so that implementing classes cannot return dictionaries which would allow modification. It just feels like if the interface is returning a collection which should be read only, then the return type should reflect that. By specifying the generic IDictionary<...> return type, methods which use the interface may try to modify the dictionary only to run into a NotSupportedException.

Any suggestions on how to resolve this are appreciated!

UPDATE As it turns out, the real issue here is with the ReadOnlyCollection. It simply should be replaced by IEnumerable and it simplifies much of the work converting between various return types.

See ReadOnlyCollection or IEnumerable for exposing member collections? and notice Jon Skeet's answer. This is why I love SO :)

Upvotes: 0

Views: 129

Answers (2)

supercat
supercat

Reputation: 81247

My inclination would be to define interfaces: DoubleKeyLookup<out ValT>, and derived from that, DoubleKeyLookup<in KeyT1, in KeyT2, out ValT>. The first would have methods like GetSequence(Object key1, Object key2);, TryGetSequence, and GetSequenceOrEmpty (the former would throw on not-found; the second would return null; the third would return Enumerable<ValT>.Empty). The second would be similar, but with 'key' parameters of the specified types. Most consumers would probably use the second, but the first would be usable in case one wanted to e.g. see if a particular Animal was in a dictionary where all the keys were Cat.

Upvotes: 1

Ani
Ani

Reputation: 10906

The simplest and most user-friendly way I can think of is:

Since ReadOnlyDictionary implements IDictionary<,> but throws exceptions on unsupported operations, I would recommend that the simplest and most readable way be to return a ReadOnlyDictionary<,> that implements IReadOnlyDictionary<,> instead. IReadOnlyDictionary<,> would simply derive from IDictionary<,> since they're functionally equivalent.

The name "IReadonlyDictionary" in the method prototype would then indicate to the caller that methods that alter the dictionary may not work.

Hope this helps!

Upvotes: 1

Related Questions