Covariance out T and contravariance in T
Context: Covariance (out T) allows using a more derived type than originally specified (for return values). Contravariance (in T) allows using a less derived type (for input parameters). This enables polymorphic assignment of generic interfaces.
Usage Example
Section titled “Usage Example”using System;using System.Collections.Generic;
// Covariance: T is in output position (out)public interface IProducer<out T>{ T Produce();}
// Contravariance: T is in input position (in)public interface IConsumer<in T>{ void Consume(T item);}
public class Animal { }public class Dog : Animal { }
public class DogProducer : IProducer<Dog>{ public Dog Produce() => new Dog();}
public class AnimalConsumer : IConsumer<Animal>{ public void Consume(Animal animal) { Console.WriteLine($"Consumes {animal.GetType().Name}"); }}
class Program{ static void Main() { // Covariance: IProducer<Dog> can be assigned to IProducer<Animal> IProducer<Animal> producer = new DogProducer(); Animal animal = producer.Produce(); Console.WriteLine($"Produced: {animal.GetType().Name}");
// Contravariance: IConsumer<Animal> can be assigned to IConsumer<Dog> IConsumer<Dog> consumer = new AnimalConsumer(); consumer.Consume(new Dog()); }}Output console
Section titled “Output console”dotnet runProduced: DogConsumes DogImportant notes
Section titled “Important notes”out TmeansTcan only be used as output (method return, property get).in TmeansTcan only be used as input (method parameters, property set).- Value types do not support variance.
Real-world usage example
Section titled “Real-world usage example”IEnumerable<out T> – Covariant, so you can pass List<Dog> to a method expecting IEnumerable<Animal>.
Action<in T> – Contravariant.
See .NET docs on covariance/contravariance.