ISourceCode

Make the frequent cases fast and the rare case correct

C# – Delegate Covariance and Contravariance

In Computer Science within the type system of a programming language, covariance and contravariance refers to the ordering of types from narrower to wider and their interchangeability or equivalence in certain situations (such as parameters, generics, and return types). – Wikipedia

Thus,
covariant: converting from wider (double) to narrower (float).
contravariant: converting from narrower (float) to wider (double).

In C# 2.0 covariance and contravariance was introduced for delegates. Method group to delegate conversions are covariant in return types, and contravariant in argument types – msdn FAQ

In the previous article delegates in C# were explained. How they are declared, instantiated and invoked was explained. In the article it was mentioned that the method’s argument types and return type should match the delegate’s argument types and return types. However this rule can be made flexible by use of Covariance and Contravariance.

To begin with there is a base class Planet with a method CallingPlanet() and derived class Earth with a method CallingEarth()/

In Covariance a method can be encapsulated who’s return type is directly or indirectly derived from return type of delegate.

For example

public delegate Planet CovariantDelegate( );

is a covariant delegate with return type Planet. Thus methods CallingPlanet() and CallingEarth() are encapsulated by the delegate and the return type of these methods are either of type Planet or a derived type from Planet i.e Earth. So methods returning Planet and Earth can be reference by the covariant delegate who’s return type is of the base class type i.e Planet.

In Contravariance for example a delegate

public delegate void ContravariantDelegate(Earth objEarth);

can be defined to take a method who’s parameter is Earth. If Earth derives from Planet then the delegate can encapsulate a method PlanetParamMethod(Planet objPlanet) that take Planet object as parameter. So if a delegate take a parameter of a derived type then we can use this delegate to reference a method that takes base class type as parameter. Thus enabling to call both methods one with base type parameter and the other with derived type parameter.

invocation can be done like this

ContravariantDelegate varContravariantDelegate = new ContravariantDelegate(PlanetParamMethod);
varContravariantDelegate(e);

varContravariantDelegate = new ContravariantDelegate(EarthParamMethod);
varContravariantDelegate(e);

where e is the Earth object and referenced methods are PlanetParamMethod(Planet objPlanet) and EarthParamMethod(Earth objEarth)


using System;
using System.Collections.Generic;
using System.Text;

namespace Co_ContravarianceDemo
{
    class Planet
    {
        public Planet CallingPlanet( )
        {
            Console.WriteLine("Displaying a planet base class");
            return this;
        }
    }
    class Earth : Planet
    {
        public Earth CallingEarth( )
        {
            Console.WriteLine("Displaying earth derived class");
            return this;
        }
    }
    class Program
    {
        public delegate Planet CovariantDelegate( );
        public delegate void ContravariantDelegate(Earth objEarth);

        private static void PlanetParamMethod(Planet objPlanet)
        {
            Console.WriteLine("Method for Planet object");
        }

        private static void EarthParamMethod(Earth objEarth)
        {
            Console.WriteLine("Method for Earth object");
        }

        static void Main(string[ ] args)
        {
            Planet p = new Planet( );
            Earth e = new Earth( );

            CovariantDelegate varCovariantDelegate = new CovariantDelegate(p.CallingPlanet);
            varCovariantDelegate( );

            varCovariantDelegate = new CovariantDelegate(e.CallingEarth);
            varCovariantDelegate( );

            ContravariantDelegate varContravariantDelegate = new ContravariantDelegate(PlanetParamMethod);
            varContravariantDelegate(e);

            varContravariantDelegate = new ContravariantDelegate(EarthParamMethod);
            varContravariantDelegate(e);
        }
    }
}

OUTPUT:

\bin\Debug> .\Co_ContravarianceDemo.exe
Displaying a planet base class
Displaying earth derived class
Method for Planet object
Method for Earth object

Thus summarizing as per the MSDN FAQ
When you instantiate a delegate Planet CovariantDelegate( ), you can assign it a method CallingEarth( )that has a more derived (Earth) return type than that specified in the delegate (Planet) – (covariance).

You can also assign a method PlanetParamMethod(Planet objPlanet) that has parameter types of base type (Planet objPlanet) which is less derived or parent type than those in the delegate (Earth theEarth), hence we can pass an object of the base type as a parameter where an object of derived type is expected.-(contravariance).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: