I ran into a case where the fact that List.AsReadOnly()
returns a ReadOnlyCollection
instead of an IReadOnlyCollection
made things hard for me. Since the returned collection was the Value
of a Dictionary
, it could not be automatically upcast to an IReadOnlyCollection
. This seemed strange, and upon review of the .Net source code I confirmed that the AsReadOnly()
method does something different for List
than it does for Dictionary
, namely, returning the concrete class instead of the interface.
Can anyone explain why this is? It seems a disservice to have this inconsistency, especially because we want to use the interfaces when possible and especially when public.
In my code, I was thinking at first that since my consumer was just a private method, I could change its parameter signature from IReadOnlyDictionary<T, IReadOnlyCollection<T>>
to IReadOnlyDictionary<T, ReadOnlyCollection<T>>
. But, then I realized that this makes it look like the private method might modify the collection values, so I put an annoying explicit cast into the earlier code in order to properly use the interface:
.ToDictionary(
item => item,
item => (IReadOnlyCollection<T>) relatedItemsSelector(item)
.ToList()
.AsReadOnly() // Didn't expect to need the direct cast
)
Oh, and since I'm always getting covariance and contravariance confused, could someone please tell me which one is preventing the automatic cast, and try to remind me in a sensible way how to remember them for the future? (e.g., collections are not ______variant [co/contra] for _____ [input/output] parameters.) I understand why this can't be, because there can be many implementations of the interface and it isn't safe to cast all the individual elements of the dictionary to the requested type. Unless I'm blowing even this simple aspect and I don't understand it, in which case I hope you can help set me right...