В системе типов языка программирования дисперсия определяет отношение между подтипами. Например, наиболее популярными из таких отношений являются инвариантность, ковариантность и контравариантность. Они определяются следующим образом:
- Ковариация — отношение подтипа, при котором программный модуль (класс, метод или функция) может принимать и обрабатывать тип
T
и всех его наследников. Позволяет использовать более производный тип, чем указано изначально. Вы можете назначить экземплярIEnumerable<Derived>
переменной типаIEnumerable<Base>
. - Контравариантность — отношение подтипа, при котором программный модуль (класс, метод или функция) может принимать и обрабатывать тип
T
и всех его предков. Позволяет использовать более общий (менее производный) тип, чем указано изначально. Вы можете присвоить экземплярAction<Base>
переменной типаAction<Derived>
. - Инвариантность — отношение подтипа, при котором программный модуль (класс, метод или функция) может принимать и обрабатывать только предоставленный тип
T
. Означает, что вы можете использовать только тот тип, который был указан изначально. Инвариантный параметр универсального типа не является ни ковариантным, ни контравариантным. Вы не можете присвоить экземплярList<Base>
переменной типаList<Derived>
или наоборот.
Кажется не таким уж очевидным на первый взгляд, не так ли? Лично я всегда смешиваю определения ковариантности и контравариантности. Поэтому я пишу эту статью :)
Рассмотрим ковариацию, она утверждает, что программный объект принимает тип T
среди всех своих наследников. Чтобы легко запомнить такое отношение, пусть это цепочка наследования, где стрелка вправо обозначает отношение наследования между типами.
Animal -> Cat -> Lion
Такая цепочка наследования является ковариантной для типа Animal.
Таким образом, ковариация означает отклонение в одном и том же направлении.
Примеры ковариантных модулей следующие.
Кроме того, ковариация является отношением только для чтения, в контексте C#
оно всегда содержит свойства только для чтения и обозначается как ключевое слово out
.
В отличие от ковариации, контравариантность означает отклонение в противоположном направлении. Рассмотрим наше животное, кота, льва, родственника,
Animal -> Cat -> Lion
Эта цепочка подтипов контравариантна, например, для Льва. Таким образом, этот контравариантный модуль принимает Lion и всех его предков, таких как Cat и Animal.
Контравариантность — это отношение чтение-запись. В контексте C#
он может содержать свойства только для записи, только для чтения и чтения-записи и обозначается как ключевое слово in
.
Пример контравариантного модуля выглядит следующим образом.
Однако вызов функции Push
для чего-либо еще, кроме T
, вызывает ошибку времени компиляции в C#
.