BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

.NET Framework
.NET 4.0+

Generic Variance in .NET 4.0

With the release of .NET 4.0, Microsoft introduced covariance and contravariance to generic interfaces and delegates. Generic variance allows conversion and interchangeability of types in a logical manner that was not previously possible.

Contravariant Type Example

For the final example we will create an interface with a contravariant type parameter. The example is rather contrived but serves the purpose of showing the syntax and use of contravariance in generic interfaces. The interface defines a method that calculates the monthly pay of a payee. Note the use of the in keyword for the type parameter, "T":

interface IPayCalculator<in T>
{
    decimal GetMonthlyPay(T payee);
}

The class that implements the interface and calculates the monthly pay is quite simple. The class implements IPayCalculator<T>, applying a generic constraint that ensures that the type will be an Employee or subclass thereof. This means that we can use the Salary property in the GetMonthlyPay method.

class PayCalculator<T> : IPayCalculator<T> where T : Employee
{
    public decimal GetMonthlyPay(T payee)
    {
        return Math.Round(payee.Salary / 12, 2);
    }
}

We can now create and use a PayCalculator. In the code below we calculate the monthly pay for an AreaManager, using a pay calculator held in an IPayCalculator<AreaManager>. The object performing the calculation is a PayCalculator<Employee>. This wider type is assigned to the narrower interface type. If you remove the in keyword from the interface, the code will no longer compile as this assignment requires contravariance.

IPayCalculator<AreaManager> calc = new PayCalculator<Employee>();
AreaManager sam = new AreaManager { Salary = 36000 };
Console.WriteLine("Monthly Pay = {0}", calc.GetMonthlyPay(sam));    // Monthly Pay = 3000

Simultaneous Generic Covariance and Contravariance

It is possible for an interface or delegate to be both covariant and contravariant in separate type parameters. An example of such a delegate is the Func delegate, a definition of which is shown below. In this case the T type parameter is contravariant and the TResult type parameter is covariant. It is not possible for any single type parameter to be both covariant and contravariant.

public delegate TResult Func<in T, out TResult>(T arg)
19 November 2011