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.