ZetCode

C# covariance and contravariance

last modified October 5, 2026

Covariance and contravariance describe safe conversions between constructed generic types. In C#, generic interfaces and delegates can declare variance: out marks a covariant type parameter, and in marks a contravariant one.

Suppose Dog derives from Animal. A conversion from a dog-producing type to an animal-producing type is safe, while a consumer of any animal can also consume a dog. Variance allows these conversions for compatible generic interfaces and delegates.

C# covariance example

A covariant type parameter appears only in output positions. The built-in IEnumerable<T> is covariant, so a sequence of dogs can be used where a sequence of animals is expected.

Program.cs
using System.Collections.Generic;

IEnumerable<Dog> dogs = new List<Dog> { new Dog(), new Dog() };
IEnumerable<Animal> animals = dogs;

Console.WriteLine(animals.Count());

class Animal { }
class Dog : Animal { }
$ dotnet run
2

The conversion is safe because each element obtained from the sequence is an Animal. The out annotation on IEnumerable<out T> declares this output-only relationship.

C# contravariance example

A contravariant type parameter is used in input positions. An action that accepts any animal can therefore be assigned to a variable that accepts dogs.

Program.cs
Action<Animal> describe = animal => Console.WriteLine(animal.Name);
Action<Dog> describeDog = describe;

describeDog(new Dog { Name = "Rex" });

class Animal
{
    public string Name { get; set; } = "";
}

class Dog : Animal { }
$ dotnet run
Rex

Action<in T> is contravariant. A delegate that can handle every animal can handle a dog, so the assignment is safe.

Declaring variance

Custom interfaces and delegates can declare variance for reference-type conversions. This example declares a producer with an out parameter and a consumer with an in parameter.

Program.cs
interface IProducer<out T>
{
    T Get();
}

interface IConsumer<in T>
{
    void Put(T value);
}

A covariant type parameter must be used in output positions, while a contravariant parameter must be used in input positions. A type parameter without either annotation is invariant.

Variance limitations

Source

Covariance and contravariance - C# programming guide

This article introduced covariance and contravariance in C# generics.

Author

Jan Bodnar writes programming tutorials and educational content.

List all C# tutorials.