Я столкнулся с ситуацией, когда тот факт, что List.AsReadOnly()
возвращает ReadOnlyCollection
вместо IReadOnlyCollection
, усложнил для меня все. Поскольку возвращенная коллекция была Value
Dictionary
, она не может быть автоматически повышена до IReadOnlyCollection
. Это показалось странным, и после обзора исходного кода .Net я подтвердил, что метод делает что-то другое для List
, чем для Dictionary
, а именно, возвращает конкретный класс вместо интерфейса.Почему List.AsReadOnly возвращает ReadOnlyCollection, но Dictionary.AsReadOnly возвращает IReadOnlyDictionary?
Может ли кто-нибудь объяснить, почему это так? Похоже, что это несогласованность, особенно потому, что мы хотим использовать интерфейсы, когда это возможно, и особенно когда они публичны.
В моем коде я сначала подумал, что, поскольку мой потребитель был только частным методом, я мог бы изменить его сигнатуру параметра от IReadOnlyDictionary<T, IReadOnlyCollection<T>>
до IReadOnlyDictionary<T, ReadOnlyCollection<T>>
. Но потом я понял, что это делает его похожим на частный метод может изменять значение сбора, так что я положил раздражающее явное приведение в ранее коду, чтобы правильно использовать интерфейс:
.ToDictionary(
item => item,
item => (IReadOnlyCollection<T>) relatedItemsSelector(item)
.ToList()
.AsReadOnly() // Didn't expect to need the direct cast
)
О, и так как Я всегда сбиваюсь с ковариацией и контравариантностью, может кто-нибудь, пожалуйста, скажите мне, какой из них препятствует автоматическому броску, и попытайтесь напомнить мне разумным образом, как запомнить их на будущее? (например, коллекции не ______variant [co/contra] для параметров _____ [ввода/вывода].) Я понимаю, почему этого не может быть, потому что может быть много реализаций интерфейса, и небезопасно отображать все отдельные элементы словаря запрашиваемого типа. Если я не вдуваю даже этот простой аспект, и я не знаю, как это сделать, а я не знаю поймите его, и в этом случае я надеюсь, что вы сможете помочь мне правильно ...
О, о, о! Я предполагал, что он не будет автоматически отбрасываться, потому что он * не был ковариантным, но теперь я вижу, что именно потому, что 'IReadOnlyCollection' читается только, компилятор может видеть, что он безопасно отбрасывать из производного типа в базу , Так что, если это так, почему я должен сделать явный бросок? – ErikE
Ковариация и контравариантность выполняются только при использовании двух общих * интерфейсов * и изменении параметра * типа * (от базового до производного или наоборот). Листинг, который вы использовали, - это просто бросок - это разрешено, потому что 'ReadOnlyCollection' реализует 'IReadOnlyList '. –
О, упс, это имеет смысл. Это становится яснее. Почему он не может быть неявно брошен? – ErikE