Covariance Vs Contravariance

Covariance and Contravariance are the two new features introduced by C# 2.0 in which, delegate objects support the notion of covariance on their return type and contravariance on their arguments.

Covariance enables a method to be assigned to a delegate when the method's return type is a class derived from the class specified by the return type of the delegate.

Contravariance enables a method to be assigned to a delegate when a method's parameter type is a base class specified by the delagate's declaration.

Example:


// Demonstrate covariance and contravariance.
using System;

class ReturnX
{
  public int iValue;
}

// ArgumentY is derived from ReturnX.
class ArgumentY : ReturnX { }

// This delegate returns ReturnX and takes an ArgumentY argument.
delegate ReturnX ChangeIt(ArgumentY obj);

class CoContraVariance
{
  // This method returns ReturnX and has an ReturnX parameter.
  static ReturnX IncrA(ReturnX obj)
  {
    ReturnX oReturnX = new ReturnX();
    oReturnX.iValue = obj.iValue + 1;
    return oReturnX;
  }

  // This method returns ArgumentY and has a ArgumentY parameter.
  static ArgumentY IncrB(ArgumentY obj)
  {
    ArgumentY oArgumentY = new ArgumentY();
    oArgumentY.iValue = obj.iValue + 1;
    return oArgumentY;
  }

  static void Main()
  {
    ArgumentY oArgumentY = new ArgumentY();
    // In this case, the parameter to IncrA is ReturnX and the parameter to ChangeIt is 
    // ArgumentY.
    // Because of contravariance, the following line is OK.
    ChangeIt oChangeIt = IncrA;
    ReturnX oReturnX = oChangeIt(oArgumentY);
    Console.WriteLine("oReturnX: " + oReturnX.iValue);

    // In the next case, the return type of IncrB is ArgumentY and the return type of 
    // ChangeIt is ReturnX.
    // Because of covariance, the following line is OK.
    oChangeIt = IncrB;
    oArgumentY = (ArgumentY) oChangeIt(oArgumentY);
    Console.WriteLine("oArgumentY: " + oArgumentY.iValue);
  }
}

The output from the program is shown here:
oReturnX: 1
oArgumentY: 1


Covariance and Contravariance – VS2010 C# - Part 1


Covariance and Contravariance – VS2010 C# - Part 2



Covariance and Contravariance – VS2010 C# - Part 3



Related Post: DelegatesMulticast Delegates (Multicast Delegates)Anonymous Methods

Why are delegates used?

All delegates are classes that are implicitly derived from System.Delegate. In general, delegates are useful for two main reasons:
  • Delegates support events.
  • Delegates give your program a way to execute methods at runtime without having to know precisely what those methods are at compile time. This ability is quite useful when you want to create a framework that allows components to be plugged in.
Related Post: Delegates, Multicast Delegates (Multicast Delegates)Anonymous Methods

Anonymous Methods

Anonymous methods allow you to declare a method body without an explicit name. They behave exactly like normal methods behind the scenes, however, there is no way to explicitly call them in your code.

An anonymous method is a block of code that is passed to a delegate. It is created only to be used as a target for a delegate. The main advantage to using an anonymous method is simplicity. It is a preferred way for writing inline code.

You can omit the parameter list in anonymous methods. Thus an anonymous method can be converted to delegates with a variety of signatures, which is not possible with lambda expressions.

Example:

// Demonstrate an anonymous method
using System;

// Declare a delegate type
delegate void ShowSum();

class AnonymousMethod
{
static void Main()
{
// The code for generating the sum is passed as an anonymous method
ShowSum oShowSum = delegate
{
// This is the block of code passed to the delegate
int iSum = 0;
for (int i = 0; i <= 5; i++)
{
iSum += i;
}
Console.WriteLine(iSum);
Console.ReadKey();
}; // Notice the semicolon
oShowSum();
}
}
 
Output: 15

Pass Arguments and Return a Value from an Anonymous Method

 

It is possible to pass one or more arguments to an anonymous method. To do so, follow the delegate keyword with a parenthesized parameter list. Then, pass the argument(s) to the delegate instance when it is called.

An anonymous method can also return a value. However, the type of the return value must be compatible with the return type specified by the delegate.

Example:


// Demonstrate an anonymous method that returns a value.
using System;
// This delegate returns a value. Notice that ShowSum now has a parameter.
delegate int ShowSum(int iEnd);
class AnonymousMethod
{
  static void Main()
  {
    int result;   
    // Here, the ending value for the count is passed to the anonymous method.
    // A summation of the count is returned.

    ShowSum oShowSum = delegate (int iEnd)
    {
       int iSum = 0;
       for(int i=0; i <= iEnd; i++)
       {
         Console.WriteLine(i);
         iSum += i;
       }
       return iSum;  // Return a value from an anonymous method
    };

    result = oShowSum(3);
    Console.WriteLine("Summation of 3 is " + result);
    Console.WriteLine();

    result = oShowSum(5);
    Console.WriteLine("Summation of 5 is " + result);

  }
}

Related Post: Delegates, Multicast Deletages (Multicasting)

Shallow Copy Vs Deep Copy

Shallow Copy


For instances of value types, the '=' assignment operator copies the state of the source object to the destination object. The copy is done byte-by-byte. Simple object copying by members is achievable through the method MemberwiseClone() inherited from System.Object. This is a protected method and the copying supplied by it is known as shallow copying (it copies the reference but not the referred object; thus the original object and its copy/clone refer to the same object).

Steps:

  • Create a new object 
  • Copy the non-static fields of the current object to the new object
  • For a value type field, copy is done byte-by-byte
  • For a reference type, shallow copy copies the reference but not the referred object

Example:

public class ShallowCopy
{
  public int iValue;
}

public class CloneMe
{
  public ShallowCopy oShallowCopy = new ShallowCopy();

  public CloneMe(int iNewValue)
  {
    oShallowCopy.iValue = iNewValue;
  }

  public object GetCopy()
  {
    return MemberwiseClone();
  }
}

In this case, the shallow copy obtained through GetCopy() will have a field that refers to the same object as the original.

The following code demonstrates using this class:

CloneMe oCloneMeSource = new CloneMe(10);
CloneMe oCloneMeTarget = (CloneMe)oCloneMeSource.GetCopy();
Console.Writeline("oCloneMeTarget - {0}", oCloneMeTarget.oShallowCopy.iValue);  // Output: oCloneMeTarget – 10
oCloneMeSource.oShallowCopy.iValue = 6;
Console.Writeline("oCloneMeTarget - {0}", oCloneMeTarget.oShallowCopy.iValue);  // Output: oCloneMeTarget – 6


Deep Copy


For instances of reference type, the '=' assignment operator copies the reference, and not the object. To copy the state of an object of reference type, we can use deep copying. Deep Copying is achieved by implementing the method Clone() which is the only method in System.ICloneable interface. This method returns a value of type System.Object.

Steps:
  • Create a new object
  • Copy the nonstatic fields of the current object to the new object
  • For a value type field, copy is done byte-by-byte
  • For a reference type, a new copy of the referred object is created

Example:

public class DeepCopy
{
  public int iValue;
}

public class CloneMe : ICloneable
{
  public DeepCopy oDeepCopy = new DeepCopy();

  public CloneMe(int iNewValue)
  {
     oDeepCopy.iValue = iNewValue; 
  }

  public object Clone()
  {
    CloneMe oCloneMe = new CloneMe(oDeepCopy.iValue);
    return oCloneMe;
  }
}

A new CloneMe object using the iValue field of the DeepCopy object contained in the original CloneMe object (oDeepCopy) is created here. This field is a value type, so no deep copying is necessary.

The following code demonstrates using this class:

CloneMe oCloneMeSource = new CloneMe(10);
CloneMe oCloneMeTarget = (CloneMe)oCloneMeSource.Clone();
Console.Writeline("oCloneMeTarget - {0}", oCloneMeTarget.oDeepCopy.iValue);  // Output: oCloneMeTarget – 10
oCloneMeSource.oDeepCopy.iValue = 6;
Console.Writeline("oCloneMeTarget - {0}", oCloneMeTarget.oDeepCopy.iValue);  // Output: oCloneMeTarget – 10

This time, the contained objects are independent.

Multicast Deletages (Multicasting)

Multicasting is the ability to create an invocation list, or chain, of methods that will be automatically called when a delegate is invoked.

Steps:
  • Instantiate a delegate
  • use the + or += operator to add methods to the chain
  • use the or –= operator to remove methods from the chain 
If the delegate returns a value, then the value returned by the last method in the list becomes the return value of the entire delegate invocation. 

Example:

// Multicasting Demo
using System;
// Declare a delegate type
delegate void ModifyString(ref string sTestString);

class MultiCastDemo
{
  // Replaces spaces with dollars
  static void ReplaceSpaces(ref string sTestString)
  { 
    sTestString = sTestString.Replace(' ', '$');
    Console.WriteLine("Replaced spaces with dollars.");
  }
  // Remove spaces
  static void RemoveSpaces(ref string sTestString)
  {
    string temp = "";
    int i;
    for(i=0; i < sTestString.Length; i++)
      if(sTestString[i] != ' ') temp += sTestString[i];
    sTestString = temp;
    Console.WriteLine("Removed spaces.");
  }
  // Reverse a string
  static void Reverse(ref string sTestString)
  {
    string temp = "";
    int i, j;
    for(j=0, i= sTestString.Length-1; i >= 0; i--, j++)
      temp += sTestString[i];
    sTestString = temp;
    Console.WriteLine("Reversed string.");
  }

  static void Main()
  {
    // Construct delegates
    ModifyString oModifyString;
    ModifyString oModifyStringReplaceSpaces = ReplaceSpaces;  // Method Group Conversion
    ModifyString oModifyStringRemoveSpaces = RemoveSpaces;  // Method Group Conversion
    ModifyString oModifyStringReverseString = Reverse;  // Method Group Conversion
    string sString = "This is a test for Multicast Delegates";
   
    // Set up multicast
    oModifyString = oModifyStringReplaceSpaces;
    oModifyString += oModifyStringReverseString;
   
    // Call multicast
    oModifyString(ref sString);
    Console.WriteLine("Resulting string: " + sString);
    Console.WriteLine();
   
    // Remove replace and add remove
    oModifyString -= oModifyStringReplaceSpaces;
    oModifyString += oModifyStringRemoveSpaces;
    sString = "This is a test."; // reset string
   
    // Call multicast
    oModifyString(ref sString);
    Console.WriteLine("Resulting string: " + sString);
    Console.WriteLine();
  }
}

Related Post: Delegates

Delegates


A delegate provides a way to encapsulate a method. It is an object that can refer to a method and can invoke the method to which it refers.

Delegates provide a built-in, language-supported mechanism for defining and executing callbacks. It provides an excellent mechanism to decouple the method being called from the actual caller. In fact, the caller of the delegate has no idea whether it is calling an instance method or a static method. To the caller, it is calling arbitrary code.

The principal advantage of a delegate is that the method that will be invoked by a delegate is not determined at compile time, but rather at runtime.

A delegate dynamically wires up a method caller to its target method. There are two aspects to a delegate: type and instance.

A delegate type defines a protocol to which the caller and target will conform, comprising a list of parameter types and a return type. A delegate instance is an object that refers to one or more target methods conforming to that protocol.

Delegate declarations look almost exactly like abstract method declarations, except for the delegate keyword. Ex:

public delegate double ComputeOutput( int iValueX, int iValueY );

When you instantiate an instance of a delegate, you must wire it up to a method to call when it is invoked. The method that you wire it up to could be either a static or an instance method that has a signature compatible with that of the delegate.

Thus, the parameter types and the return type must either match the delegate declaration or be implicitly convertible to the types in the delegate declaration.

using System;

public delegate double ComputeOutput( int iValueX, int iValueY ); // Delegate declaration

public class Computer
{
  public Computer ( double dFactor ) // Constructor   
  {
    this.dFactor = dFactor;
  }
  public double ComputeInstance( int iValueX, int iValueY ) // instance method
  {
    double dResult = (iValueX + iValueY) * dFactor;
    Console.WriteLine( "Instance Results: {0}", dResult );
    return dResult;
  }
  public static double ComputeStatic( int iValueX, int iValueY ) // static method
  {
    double dResult = (iValueX + iValueY) * 0.5;
    Console.WriteLine( "Static Result: {0}", dResult );
    return dResult;
  }
  private double dFactor;
}

public class MyComputer
{
  static void Main()
  {
    Computer oComputer1 = new Computer( 0.69 );
    Computer oComputer2 = new Computer( 0.76 );
    ComputeOutput delegate1 = new ComputeOutput (oComputer1.ComputeInstance );
    ComputeOutput delegate2 = new ComputeOutput (oComputer2.ComputeInstance );
    // points to a static method - use method group conversion
    ComputeOutput delegate3 = Computer.ComputeStatic; 
    double dTotal = delegate1( 7, 8 ) + delegate2( 9, 2 ) + delegate3( 4, 3 );
    Console.WriteLine( "Output: {0}", dTotal );
  }
}

Method Group Conversion allows you to simply assign the name of a method to a delegate, without using new or explicitly invoking the delegate’s constructor. 

Computer.StaticCompute is actually called a method group because the method could be overloaded and this name could refer to a group of methods. In this case, the method group Computer.StaticCompute has one method in it.
 
C# allows you to directly assign a delegate from a method group. When you create the delegate instances via new, you pass the method group in the constructor.

Introduction to Delegates



Related Post: Multicast Delegates (Multicasting)