2014-08-29 2 views
5

Причины этот код работает, в связи с тем, что Enumerator не может изменять коллекцию:Почему ковариация не допускается с помощью ReadOnlyCollection?

var roList = new List<string>() { "One", "Two", "Three" }; 
IEnumerable<object> objEnum = roList; 

Если попытаться сделать то же самое с List<object>, вместо IEnumerable<object>, мы получим, говорящие об ошибке компилятора что мы не можем неявно преобразовывать тип List<string> в List<object>. Хорошо, это имеет смысл, и это было хорошим решением Microsoft, на мой взгляд, как урока, извлеченного из массивов.

Однако, что я не могу понять, почему в мире это правило жесткой нормы применимо к чему-то вроде ReadOnlyCollection? Мы не сможем изменить элементы в ReadOnlyCollection, так что же касается безопасности, которая заставила Microsoft помешать нам использовать ковариацию с чем-то, что доступно только для чтения? Есть ли способ изменения такого типа коллекций, который Microsoft пытается учесть, например, с помощью указателей?

+1

«Урок, извлеченный из массивов»: в отношении ковариации массива это было фактически сознательное дизайнерское решение, позволяющее ему, потому что у него это было. Это оказалось неплохой идеей ... http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array- covariance.aspx –

+0

@ThomasLevesque Хорошо читайте, спасибо. –

ответ

7

Разница и контравариантность работают только на интерфейсах и делегатах, а не на классах.

Однако, вы можете сделать это (с использованием .NET 4.5):

ReadOnlyCollection<string> roList = ... 
IReadOnlyCollection<object> objects = roList; 

(потому что IReadOnlyCollection<T> ковариантен)


Чтобы ответить на вопрос о том, почему разница не допускается по классам , вот объяснение Джона Скита из его книги C# in Depth (второе издание, §13.3.5, стр. 394). не

NO отклонения для ТИПА ПАРАМЕТРОВ В КЛАССАХ

только интерфейсы и делегаты могут иметь параметры типа вариант. Даже , если у вас есть класс, который использует только параметр типа для ввода (или использует его только для вывода), вы не можете указывать модификаторы или out . Например, Comparer<T>, общая реализация IComparer<T>, является инвариантной - нет конверсии от Comparer<IShape> до Comparer<Circle>.

Не считая каких-либо трудностей с реализацией, которые могли бы возникнуть у , я бы сказал, что это концептуально понятное понятие. Интерфейсы представляют собой способ взглянуть на объект с определенной точки зрения , тогда как классы более внедрены в фактический тип объекта . Этот аргумент несколько ослабляется путем наследования, позволяя вам рассматривать объект как экземпляр любого из классов в нем. иерархия наследования, по общему признанию. В любом случае, CLR не разрешает .

+0

Вы правы.Я протестировал его, и он работает с интерфейсами только для чтения 'IReadOnlyCollection ' и 'IReadOnlyList '. Какая-то конкретная причина, почему она не работает с классами? –

+0

@ B.K., Я не знаю, но я предполагаю, что это связано с некоторыми внутренними деталями реализации CLR ... –

+0

Возможно, проще реализовать поддержку интерфейсов. Когда вы применяете конкретный тип к базовому конкретному типу, теперь базовый тип должен решить, следует ли ему называть его методы или базовые типы, которые зависят от сценариев виртуального/переопределения/скрытия, которые определяются только в реальном наследовании. A ReadOnlyCollection не наследует от ReadOnlycollection , поэтому сложно определить, что происходит. Интерфейсы всегда передают вызовы конкретному типу, и нет никакой двусмысленности в отношении того, вызвана ли базовая или производная. Гораздо понятнее, что означает доступ к ковариантному интерфейсу. IMO – AaronLS

Смежные вопросы