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


в чем разница между ковариацией и upcasting, или, точнее, почему они дали разные имена?

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

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;

это действительно так просто?

6 60

6 ответов:

это действительно так просто?

ковариация не связана с повышением, хотя я понимаю, почему вы думаете, что это связано.

ковариация - это следующая очень простая идея. Допустим, у вас есть переменная derivedSequence типа IEnumerable<Derived>. Допустим, у вас есть переменная baseSequence типа IEnumerable<Base>. Вот,Derived происходит от Base. Затем, с ковариацией, следующее является юридическим назначением, и происходит неявное преобразование ссылки:

baseSequence = derivedSequence;

обратите внимание, что это не upcasting. Это не тот случай, что 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> в C# 4.0 неявное преобразование ссылок из экземпляров IEnumerable<Derived> до IEnumerable<Base> если Derived происходит от Base. Направление совместимости назначения сохраняется, и именно поэтому мы говорим, что IEnumerable<T> является ковариантным по своему параметру типа.

литье относится к изменению статического типа объекты и выражений.

дисперсия относится к взаимозаменимость или эквивалентность типов в некоторых ситуациях (например, параметры, универсальные и возвращаемые типы).

IEnumerable<string> не является производным от IEnumerable<object>, Так что бросок между ними не является upcasting. IEnumerable является ковариантным по своему параметру типа и строке - это производный от объекта, поэтому приведение разрешено.

почему они разных понятий, что, в отличие от upcasting, ковариация не всегда разрешено. Проектировщикам системы типов было бы легко сделать IList<Cat> считается "производные" С IList<Animal>, но потом мы столкнулись с проблемами:

IList<Cat> cats = new List<Cat>();
IList<Animal> animals = cats; 
animals.Add(new Dog()); //Uh oh!

если бы это было разрешено, теперь наш!--4--> список будет содержать Dog!

в противоположность IEnumerable<T> интерфейс не имеет возможности добавлять элементы, так что это совершенно верно (в C# 4.0):

IList<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
//There's no way to add things to an IEnumerable<Animal>, so here we are ok

сообщение в блоге ниже имеет хорошее объяснение этого:

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

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