В системе типов языка программирования дисперсия определяет отношение между подтипами. Например, наиболее популярными из таких отношений являются инвариантность, ковариантность и контравариантность. Они определяются следующим образом:

  • Ковариация — отношение подтипа, при котором программный модуль (класс, метод или функция) может принимать и обрабатывать тип 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#.

Рекомендации