Covariance and Contravariance in Delegates (C# Programming Guide) 

Covariance and contravariance provide a degree of flexibility when matching delegate methods with delegate signatures. Covariance permits a method with a derived return type to be used as the delegate, and contravariance permits a method with derived parameters to be used as the delegate. This ability enables the creation of more flexible delegate methods, able to cope with more than a specific class or event.

Covariance

When a delegate method has a return type that is more derived than the delegate signature, it is said to be covariant. Because the method's return type is more specific than the delegate signature's return type, it can be implicitly converted. The method is therefore acceptable for use as a delegate.

Covariance enables delegate methods to be created that can be used by both classes and derived classes.

Example 1

This example demonstrates how delegates can use derived return types. The data type returned by SecondHandler is of type Dogs, which is a subset of the delegate signature return type Mammals. Therefore, all the possible values returned by SecondHandler could be stored in a variable of type Mammals.

class Mammals
{
}

class Dogs : Mammals
{
}

class Program
{
    // Define the delegate.
    public delegate Mammals HandlerMethod();

    public static Mammals FirstHandler()
    {
        return null;
    }

    public static Dogs SecondHandler()
    {
        return null;
    }

    static void Main()
    {
        HandlerMethod handler1 = FirstHandler;

        // Covariance allows this delegate.
        HandlerMethod handler2 = SecondHandler;
    }
}

Contravariance

When a delegate method signature has one or more parameters of types that are derived from the types of the method parameters, that method is said to be contravariant. As the delegate method signature parameters are more specific than the method parameters, they can be implicitly converted when passed to the handler method.

Contravariance therefore makes it easier to create more general delegate methods that can be used with a larger number of classes.

Example 2

This example demonstrates how delegates can use parameters of a type that are derived from the delegate signature parameter type. Because the delegate is required to support a parameter of type Dogs, we know that the handler with a parameter of type Mammals must be acceptable, because Dogs are a subset of Mammals.

class Mammals
{
}

class Dogs : Mammals
{
}

class Program
{
    public delegate void HandlerMethod(Dogs sampleDog);

    public static void FirstHandler(Mammals elephant)
    {
    }

    public static void SecondHandler(Dogs sheepDog)
    {
    }

    static void Main(string[] args)
    {
        // Contravariance permits this delegate.
        HandlerMethod handler1 = FirstHandler;

        HandlerMethod handler2 = SecondHandler;
    }
}

See Also

Reference

Generic Delegates (C# Programming Guide)

Concepts

C# Programming Guide
Events (C# Programming Guide)
Delegates (C# Programming Guide)