Разница между ковариацией и 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 ответов:
это действительно так просто?
ковариация не связана с повышением, хотя я понимаю, почему вы думаете, что это связано.
ковариация - это следующая очень простая идея. Допустим, у вас есть переменная
derivedSequence
типаIEnumerable<Derived>
. Допустим, у вас есть переменнаяbaseSequence
типаIEnumerable<Base>
. Вот,Derived
происходит отBase
. Затем, с ковариацией, следующее является юридическим назначением, и происходит неявное преобразование ссылки:baseSequence = derivedSequence;
обратите внимание, что это не upcasting. Это не тот случай, что
F от типов к типам (я обозначу это сопоставлениеIEnumerable<Derived>
происходит отIEnumerable<Base>
. Скорее, это ковариация, которая позволяет присвоить значение переменнойderivedSequence
переменнойbaseSequence
. Идея заключается в том, что переменные типаBase
может быть назначается из объектов типаDerived
, и сIEnumerable<T>
является ковариантным по своему параметру, объекты типаIEnumerable<Derived>
может быть присвоено переменным типаIEnumerable<Base>
.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
из того, что я могу собрать, ковариация устраняет необходимость в явном понижении после предыдущего повышения. Как правило, если вы повышаете объект, вы можете получить доступ только к методам и атрибутам базового типа, с ковариацией, похоже, вы можете подразумевать понижение, заменяя меньшие производные типы более производными типами в объявлении более производного класса.