Разница между ковариацией и повышением

В чем разница между ковариацией и повышением, или, точнее, почему им присваиваются разные имена?

Я видел следующий пример, называемый "повышением":

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

Ответ 6

Из того, что я могу собрать, ковариация устраняет необходимость явного downcasting после предыдущего upcast. Обычно, если вы поднимаете объект, вы можете получить доступ только к методам и атрибутам базового типа, с ковариацией, кажется, что вы можете указать на понижение путем замены меньших производных типов на более производные типы в более производном объявлении класса.