Разница между ковариацией и повышением
В чем разница между ковариацией и повышением, или, точнее, почему им присваиваются разные имена?
Я видел следующий пример, называемый "повышением":
string s = "hello";
object o = s; //upcast to 'string' to 'object'
В то время как следующее, которое я видел, называется "ковариация":
string[] s = new string[100];
object[] o = s;
IEnumerable<string> ies = new List<string>();
IEnumerable<object> ieo = ies;
Теперь, к моему неподготовленному взгляду, ковариация, похоже, такая же, как и повышение, за исключением того, что она относится к кастингу коллекций. (И подобное утверждение может быть сделано относительно контравариантности и понижения).
Это действительно так просто?
Ответы
Ответ 1
Теперь, к моему неподготовленному взгляду, ковариация, похоже, такая же, как и повышение, за исключением того, что она относится к кастингу коллекций. (И подобное утверждение может быть сделано относительно контравариантности и понижения).
Это действительно так просто?
Ковариация не связана с повышением, хотя я могу понять, почему вы думаете, что это связано.
Ковариация касается следующей очень простой идеи. Скажем, у вас есть переменная derivedSequence
типа IEnumerable<Derived>
. Скажем, у вас есть переменная baseSequence
типа IEnumerable<Base>
. Здесь Derived
происходит от Base
. Затем, с ковариацией, следующее правовое назначение, и происходит неявное обращение ссылки:
baseSequence = derivedSequence;
Обратите внимание, что это не повышается. Это не тот случай, когда IEnumerable<Derived>
происходит от IEnumerable<Base>
. Скорее, это ковариация, которая позволяет вам присваивать значение переменной derivedSequence
переменной baseSequence
. Идея состоит в том, что переменные типа Base
могут быть назначены из объектов типа Derived
, а так как IEnumerable<T>
является ковариантным по своему параметру, объектам типа IEnumerable<Derived>
могут присваиваться переменные типа IEnumerable<Base>
.
Конечно, я еще не объяснил, что такое ковариация. В общем, ковариация касается следующей простой идеи. Скажем, у вас есть сопоставление F
от типов к типам (я обозначу это сопоставление с помощью F<T>
, учитывая тип T
его образ при отображении F
равен F<T>
.) Скажем, что это отображение имеет следующее особое свойство:
если X
- это присвоение, совместимое с Y
, то F<X>
также совместимо с F<Y>
.
В этом случае мы говорим, что F
ковариантно по своему параметру T
. (Здесь, чтобы сказать, что "A
является присвоением, совместимым с B
", где A
и B
являются ссылочными типами, означает, что экземпляры B
могут храниться в переменных типа A
.)
В нашем случае IEnumerable<T>
в С# 4.0 неявное ссылочное преобразование из экземпляров IEnumerable<Derived>
в IEnumerable<Base>
, если Derived
получено из Base
. Направление совместимости присваивания сохраняется, и поэтому мы говорим, что IEnumerable<T>
является ковариантным в своем параметре типа.
Ответ 2
Кастинг относится к изменению статического типа объектов и выражений.
Отклонение относится к взаимозаменяемости или эквивалентности типов в определенных ситуациях (таких как параметры, дженерики и типы возврата).
Ответ 3
IEnumerable<string>
не выводится из IEnumerable<object>
, поэтому отливка между ними не повышается. IEnumerable является ковариантным в своем параметре типа, а строка -, полученная из объекта, поэтому допускается публикация.
Ответ 4
Причина, по которой они являются разными понятиями, заключается в том, что, в отличие от восходящего, ковариация не всегда разрешена. Разработчикам типовой системы было бы легко сделать IList<Cat>
считаться "производным" от IList<Animal>
, но тогда мы сталкиваемся с проблемами:
IList<Cat> cats = new List<Cat>();
IList<Animal> animals = cats;
animals.Add(new Dog()); //Uh oh!
Если это было разрешено, теперь наш cats
список будет содержать Dog
!
Напротив, интерфейс IEnumerable<T>
не имеет возможности добавлять элементы, поэтому это совершенно верно (в С# 4.0):
IList<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
//There no way to add things to an IEnumerable<Animal>, so here we are ok
Ответ 5
В блоге Thie ниже приведено хорошее объяснение:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
Ответ 6
Из того, что я могу собрать, ковариация устраняет необходимость явного downcasting после предыдущего upcast. Обычно, если вы поднимаете объект, вы можете получить доступ только к методам и атрибутам базового типа, с ковариацией, кажется, что вы можете указать на понижение путем замены меньших производных типов на более производные типы в более производном объявлении класса.