Ковариантность и Контравариантность в Обобщениях (начало)
Очень обширная тема, поэтому разобью её на четыре поста:
1. Общие понятия
2. Вариантность в обобщённых интерфейсах
3. Вариантность в обобщённых делегатах
4. Определение вариантных интерфейсов и делегатов
1. Общие понятия
Ковариантность и контравариантность относятся к возможности использования более конкретного (производного) типа или более общего (базового) типа, чем изначально обозначенный. Обобщённые параметры типа поддерживают ковариантность и конртавариантность, чтобы предоставлять большую гибкость в присвоении и использовании обобщённых типов. В следующих примерах допустим, что
Animal – базовый класс, а Cat – производный от него.Инвариантность означает, что вы можете использовать только изначально определённый тип.
Ковариантность позволяет вам использовать производный (более конкретный) тип, чем изначально обозначенный. Вы можете присвоить экземпляр
IEnumerable<Cat> переменной типа IEnumerable<Animal>.Ковариантные параметры типа позволяют вам делать присваивания, похожие на обычный полиморфизм:
IEnumerable<Cat> cats = new List<Cat>();
IEnumerable<Animal> animals = cats;
Класс
List<T> реализует интерфейс IEnumerable<T>, поэтому List<Cat> реализует IEnumerable<Cat>. А ковариантный параметр типа делает всё остальное.Котравариантность позволяет вам использовать более общий (базовый) тип, чем изначально обозначенный. Вы можете присвоить экземпляр делегата
Action<Animal> переменной типа Action<Cat>.Контравариантность выглядит контринтуитивной. В следующем примере создаётся делегат типа
Action<Animal>, а затем он присваивается переменной типа Action<Cat>.Action<Animal> actAnimal = (target)=>{Console.WriteLine(target.GetType().Name);};
Action<Cat> actCat = actAnimal;
actCat(new Cat()); // выводит “Cat”Лямбда-выражение создаёт метод-делегат, который принимает один параметр типа
Animal, и у которого нет возвращаемого значения. Этот делегат может быть присвоен переменной типа Action<Cat>, поскольку параметр T делегата Action<T> контравариантен. Когда делегат типа Action<Animal> вызывается так, как если бы он был типа Action<Cat>, его аргумент должен быть типа Cat. В общем случае ковариантный параметр типа может быть использован в качестве типа возвращаемого значения делегата, а контравариантный – как тип параметра. В случае интерфейсов ковариантные параметры типа могут быть использованы как типы возвращаемых значений методов интерфейса, а контравариантные – как типы параметров методов интерфейса.
Продолжение следует…
Источник: https://docs.microsoft.com/ru-ru/dotnet/standard/generics/covariance-and-contravariance