C# Contravariance

Summary: in this tutorial, you’ll learn about C# contravariance and how to use it to make your code more flexible.

Introduction to C# Contravariance

Contravariance is a mechanism in C# that allows a method to accept a parameter of a less specific type than what is defined in the method signature.

In other words, contravariance allows a method to accept an argument of a base class or interface type, even if the parameter type is a derived class or interface type. C# uses the in keyword to define an interface as a contravariance.

For example, you can define a generic interface as a contravariance like this:

interface MyInterface<in T>
{
    void MyMethod(T item);
}Code language: C# (cs)

The in keyword means that you can use the T as the type of parameters of methods. If you attempt to return a value of type T, you’ll get an error. For example:

interface MyInterface<in T>
{
    void MyMethod(T item);
    T ReturnItem(); // ERROR
}Code language: C# (cs)

Note that to define T as the type of a return value, you use the covariance instead.

C# contravariance example

The following example demonstrates how contravariance works.

using static System.Console;

class Person
{
    public string Name
    {
        get; set;
    }
    public Person(string name)
    {
        Name = name;
    }

}

class Employee : Person
{
    public string JobTitle
    {
        get; set;
    }

    public Employee(string name, string jobTitle) : base(name)
    {
        JobTitle = jobTitle;
    }

}

interface IGroup<in T>
{
    void Add(T item);

    int Count
    {
        get;
    }
}

class Group<T> : IGroup<T>
{
    private readonly List<T> list = new();

    public int Count => list.Count;

    public void Add(T item) => list.Add(item);

}

class Program
{
    public static void Display(IGroup<Employee> group)
    {
        WriteLine(group.Count);
    }

    public static void Main(string[] args)
    {
        IGroup<Person> group = new Group<Person>();
        group.Add(new Person("John Doe"));
        group.Add(new Person("Jane Doe"));

        Display(group);
    }
}Code language: C# (cs)

Output:

2

How it works.

First, define the Person class that has the Name property, and the Employee class that inherits from the Person class. The Employee class has additional JobTitle property.

Next, define the IGroup<T> interface as contravariance by using the keyword in. The IGroup<T> has a method Add() and a Count property.

Then, define the Group<T> class that implements the IGroup<T> interface. The Add() method adds an item with type T to a list and the Count property returns the number of items in the list.

After that, define the Display method that accepts an instance of IGroup<Employee> and shows the number of items in the group to the console.

Finally, create a group of Person objects and pass it to the Display method. Because the IGroup<T> is a contravariance, the program run successfully.

But if you take out the in keyword from the IGroup<T> interface, the program won’t be compiled.

Summary

  • In C#, contravariance is a feature that allows a method to take an argument that is less specific than the one defined in the method signature.
  • Use the keyword in to define an interface as a contravariance.
Was this tutorial helpful ?