Covariance and Contra-Variance in Delegates

If it comes to day to day normal programming with C# language then delegates by far are the most complicated thing to understand.Delegates add the asynchronous programming model to .Net Framework.Working with delegates involve following steps :

  • Declare a delegate type
  • Create an instance of declared delegate type
  • While creating an instance of delegate type , pass the method which needs to be invoked

So following is one sample delegate type declaration

internal delegate void DelegateDemo(string name)

Now lets assume that following is the function which we need to invoke via delegates

void PrintName(string name)
{
// Code goes here ...
}

Now create an instance of previously defined delegate type and invoke the corresponding method

// Creating instance of delegate
DelegateDemo demo = new DelegateDemo(PrintName);

//Invoke the method
demo("Hello");

Now , the above example was definitely not intended to teach how to use delegates.The important thing to notice here is that the method(PrintName) declaration is same as that of the delegate type(DelegateDemo) declaration.Both of them had void as their return type and string as the type of input parameter.

In C# 1 , this restriction was imposed from compiler that the definition of delegate type and the corresponding method must match.If in case there is a type mismatch then the compiler will throw the type mismatch error.And when I say type should match , then I mean exact match.If in delegate type declaration you have specified “Object” as return type , then you can only attach those methods which have “Object” as their return type.

In C# 2 , this restriction was greatly eased with the introduction of Covariance and Contra-Variance.Before we see an example , lets define these terms properly.

    • Covariance : It means method can return a type that is derived from delegates return type.
    • Contra-Variance : It means methods can take a parameter that is base of delegates parameter type.

If the above definition looks confusing , then have a look at the following code snippet.

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

namespace DelegateDemo
{
class Program
{
internal delegate Object PrintNames(List<;string> names);

static void Main(string[] args)
{
Console.WriteLine("------------ Covariance and Contra-Variance Demo -----------");

// Create a List
List<;string> lstNames = new List<string>();
lstNames.Add("India");
lstNames.Add("USA");
lstNames.Add("UK");
lstNames.Add("Japan");

// Create an instance of Program Type
Program pgm = new Program();

// Create an instance of Delegate Type PrintNames
PrintNames name = new PrintNames(pgm.NamePrinting);

// Invoke the delegate
name(lstNames);

Console.ReadLine();
}

private string NamePrinting(IList<;string> lstNames)
{
foreach (string strName in lstNames)
Console.WriteLine(strName);
return string.Empty;
}
}
}

Code Breakup and explanation

Following is the delegate type declaration

internal delegate Object PrintNames(List<string> names);

Note here that the return type “Object” and type of input parameter is “List<T>”.

Following is the method declaration

private string NamePrinting(IList<string> lstNames)
{
foreach (string strName in lstNames)
Console.WriteLine(strName);
return string.Empty;
}

Look carefully , the return type of the method is “string” which is actually derived from “Object” class which actually is the return type of delegate type(PrintNames).This is known as “Covariance”.And the type of input parameter of the method is “IList<T>” which is the base type for the input parameter of delegate type’s “List<T>”.This is what is known as “Contra-Variance”.

Rest of the code is fairly simple , create an instance of delegate type and pass the method as parameter and finally invoke the delegate instance.

Contact Me

rk.pawan@gmail.com | +1-737-202-7676 | LinkedIn | Facebook | Github |

No Comments