Archive for November, 2008

Covariance & Contravariance – Delegates in C#

Posted: November 30, 2008 in .NET
Tags: ,

In my last post I had discussed about delegates in C# but intentionally skipped the covariance and contravariance with respect to delegates.I would like to discuss the concepts of covariance and contravariance in general first and then correlate the same in context of C# delegates.The concept of covariance and contravariance I found a bit confusing and counterintuitive initially.

Let us consider a type/class P and let P” be a subtype/subclass of T.We will denote this relationship as P”->P for sake of discussion.Based on this inheritance relationship following statements are true:

  1. Domain of P is much broader than P”.
  2. Any context where P is expected we can substitute it by P” [Liskov Substitution Principle]

Similarly we have another pair of classes R and R” where R”->R.

Now we will consider a function f which type P as parameter and returns type R.We will denote this as R f(P).Similarly there is another function f” as R”f”(P”) which accepts P” as parameter and returns type R”.

Can we say that f”->f or we can replace f by f” in any context?

In order to replace f by f”, f” should be able to handle parameter of type P and return value of type R.

Now R” is a subclass of R,so R” is a type of R.Hence we can say f” basically returns a type R.This is covariance by which we can replace a method by another having a more derived return type.

Now let’s see can f” handle a parameter of type P.The answer is NO.The logic built into f” is only for the possible values of P” and P can have more possible values than P” as it is the super class.So f” will malfunction for all those values of P which are outside the range of P”.So f” can replace f only if parameter of f is more derived than that of f”.This is contravariance by which we can replace a method by another having parameter types that are less derived.

If we redefine f as R f(P”) and f” as R” f”(P) then we can say f”->f and can replace f in any context.

Now let us consider an example with some real life examples.

There is a base class called Order and Order can be of two derived types say SalesOrder and PurchaseOrder.Similarly there is a base class called AccountRecord and there are two derived classes say AccountPayable and AccountReceivable.

There are two methods

  1. M1 – AccountRecord GetAccountInfo(Order o)
  2. M2 – AccountPayable GetAccountInfo(PurchaseOrder po)

M2 cannot accept a variable of type O as parameter because if it is an instance of SalesOrder instead of PurchaseOrder this will fail.

But calling code which expects AccountRecord as output can very well handle both AccountPayable and AccountReceivable.

We can relate this to a delegate in the following way:

public delegate object ReadObjectDelegate(FileStream fs)

We can attach the following method to this delegate

public string Read(Stream s)

This is because Stream is less derived than FileStream and string is more derived than object

Delegates in C#

Posted: November 29, 2008 in .NET
Tags: ,

Most of us quite familiar with the delegate keyword in C#.Delegates provides us with a mechanism to encapsulate a method or multiple methods.These are quite like managed version of function pointers in C#.We normally use delegates to define callback methods.

First we need to define a delegate as a type with it’s signature as shown below.Note here the signature includes both parameters as well as return values.

public delegate void DemoDelegate(int i);

To use the delegate we need define a variable to type DemoDelegate ,instantiate the variable by passing a method name of matching signature and then invoke the delegate by passing suitable parameter values as shown in the snippet below:

class DelegateSample
   {
       public void ShowDemo()
       {
 
          DemoDelegate d = new DemoDelegate(Test); //Create instance
           d(10); //Invoke delegate
       }
       public void Test(int i)
       {
           Console.WriteLine(“Method Test called with parameter ” + i);
       }
   }

Now if we take close look into the syntactic details we see all the operation related to delegate closely resembles that of a reference type.So what’s going on inside.On taking a look into the IL code of the assembly we found a new class is generated as shown below:

.class /*02000003*/ public auto ansi sealed DelegateDemo.DemoDelegate
       extends [mscorlib/*23000001*/]System.MulticastDelegate/*01000002*/
{

……….
} // end of class DelegateDemo.DemoDelegate

So the C# compiler internally generates a class with the same name as the delegate type and it inherits from a class System.MulticastDelegate.The class System.MulticastDelegate is derived from base class System.Delegate.This method provides two public attributes

  1. Method – Metadata Method which is getting executed as an instance of System.Reflection.MethodInfo
  2. Target – Stores the instance on which delegate method executes.

Before we proceed further let us take a quick look into another feature of delegate chaining.Refer to the sample below and refer to the lines marked in red.

class DelegateSample
  {
      public void ShowDemo()
      {
          DemoDelegate d1 = new DemoDelegate(Test);
          DemoDelegate d2 = new DemoDelegate(Demo);
          Console.WriteLine(“Delegate Added”);
          DemoDelegate d3 = d1 + d2; //Two delegates are combined
          d3(10); //First method  Test and then Demo is invoked
          Console.WriteLine(“Delegate Removed”);
          DemoDelegate d4 = d3 – d1; //One delegate is removed from the combination
          d4(10);  //Only method Demo is invoked as delegate d1 is removed.
      }
      public void Demo(int i)
      {
          Console.WriteLine(“Method Demo called with parameter ” + i);
      }
      public void Test(int i)
      {
          Console.WriteLine(“Method Test called with parameter ” + i);
      }

What happens when I do + and – operation on delegates.Internally the methods System.Delegate.Combine and System.Delegate.Remove is invoked by the runtime.This we can see in the IL code of the show demo method:

.method public hidebysig instance void  ShowDemo() cil managed
{

  …………..

  IL_0028:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  ……………

  IL_0049:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,
                                                                                         class [mscorlib]System.Delegate)
  ……………..

} // end of method DelegateSample::ShowDemo