Saturday, June 27, 2026

C# Types that support Covariance and Contravariance

In this post, you will see which generic types support covariance and contra-variance and why.

Variance in C# (covariance out and contravariance in) is mainly supported on:

  • Generic interfaces
  • Generic delegates

It is not supported on generic classes or structs.

Examples:

Interface supports covariance and contravariance
interface IProducer<out T>   // covariance
{
    T Get();
}
interface IConsumer<in T>    // contravariance
{
    void Put(T item);
}

Delegate supports covariance and contravariance
delegate T Factory<out T>();
delegate void Processor<in T>(T value);

Generic Class does not support covariance and contravariance
class Box<out T> // Compile-time error
{
}

Generic Struct does not allow covariance and contravariance
struct Container<out T>   // Compile-time error
{
}

Why classes/structs don't support variance?
Classes are usually both producers and consumers of T.
Example:
class Box<T>
{
    public T Value { get; set; }
}
If covariance were allowed:
Box<Dog> dogs = new Box<Dog>();
Box<Animal> animals = dogs;   // imagine this were legal
animals.Value = new Cat();    // valid for Box<Animal>

Now dogs contains a Cat, which breaks type safety.

That is why C# restricts variance to interfaces/delegates where the compiler can enforce rules:

  • out T → only output positions (return values)
  • in T → only input positions (parameters)
Arrays are a special exception
Arrays are covariant even though they behave like classes:
string[] names = new string[2];
object[] objects = names;   // allowed
objects[0] = 10;            // Runtime exception
This compiles but throws:
ArrayTypeMismatchException

Generic variance was designed to avoid such runtime problems and provide type safety.

So the rule is:

Type Covariance/Contravariance
Interface supports
Delegate supports
Class does not support
Struct does not allow
Array supports (special case, runtime checked)

Note. The generic type parameter itself does not have to be constrained as where T : class; the actual substituted type must be a reference type for variance conversion to occur.

No comments:

Post a Comment

Hot Topics