Distinguishing variance in delegates

A generic delegate has the ability to be assigned by a method that has an unmatched signature to the delegate. We can call this variance in delegates. There are two variances in delegates, and they are covariance and contravariance. Covariance allows a method to have a return type that is more derived (subtype) than the return type that is defined in the delegate. On the other hand, contravariance allows a method to have parameter types that are less derived (supertype) than the parameter types that are defined in the delegate.

Covariance

The following is an example of covariance in delegates, which we can find in the Covariance.csproj project. First, we initialize the following delegate:

public partial class Program 
{ 
  private delegate TextWriter CovarianceDelegate(); 
} 

We now have a delegate returning the TextWriter data type. Then, we also create the StreamWriterMethod() method returning the StreamWriter object, which has the following implementation:

public partial class Program
{
  private static StreamWriter StreamWriterMethod()
  {
    DirectoryInfo[] arrDirs =
       new DirectoryInfo(@"C:Windows")
    .GetDirectories(
       "s*", 
        SearchOption.TopDirectoryOnly);

    StreamWriter sw = new StreamWriter(
    Console.OpenStandardOutput());

    foreach (DirectoryInfo dir in arrDirs)
    {
      sw.WriteLine(dir.Name);
    }

    return sw;
   }
}

We create the StringWriterMethod() method as well, returning the StringWriter object with the following implementation:

public partial class Program 
{ 
  private static StringWriter StringWriterMethod() 
  { 
    StringWriter strWriter = new StringWriter(); 
    string[] arrString = new string[]{ 
      "Covariance", 
      "example", 
      "using", 
      "StringWriter", 
      "object" 
    }; 
    foreach (string str in arrString) 
    { 
      strWriter.Write(str); 
      strWriter.Write(' '); 
    } 
    return strWriter; 
  } 
} 

Now, we have two methods returning different objects, StreamWriter and StringWriter. The return value data type of these methods is also different, with the CovarianceDelegate delegate returning the TextWriter object. However, since StreamWriter and StringWriter are derived from the TextWriter object, we can apply covariance in assigning these two methods to the CovarianceDelegate delegate.

Here is the CovarianceStreamWriterInvoke() method implementation, which assigns the StreamWriterMethod() method to the CovarianceDelegate delegate:
public partial class Program 
{ 
  private static void CovarianceStreamWriterInvoke() 
  { 
    CovarianceDelegate covDelegate; 
    Console.WriteLine( 
      "Invoking CovarianceStreamWriterInvoke method:"); 
      covDelegate = StreamWriterMethod; 
    StreamWriter sw = (StreamWriter)covDelegate(); 
    sw.AutoFlush = true; 
    Console.SetOut(sw); 
  } 
} 

In the StreamWriterMethod() method, we create StreamWriter, writing content to the console using the following code:

StreamWriter sw = new StreamWriter( 
  Console.OpenStandardOutput()); 

Then, in the CovarianceStreamWriterInvoke() method, we call this code in order to write the content to the console:

sw.AutoFlush = true; 
Console.SetOut(sw); 

If we run the CovarianceStreamWriterInvoke() method, the following output will be displayed in the console:

Covariance

From the preceding output console, we serve the list of directories we have inside the Visual Studio 2015 installation path. Indeed, you might have a different list if you installed a different version of Visual Studio.

Now, we are going to utilize the StringWriterMethod() method to create a CovarianceDelegate delegate. We create the CovarianceStringWriterInvoke() method, which has the following implementation:

public partial class Program 
{ 
  private static void CovarianceStringWriterInvoke() 
  { 
    CovarianceDelegate covDelegate; 
    Console.WriteLine( 
      "Invoking CovarianceStringWriterInvoke method:"); 
    covDelegate = StringWriterMethod; 
    StringWriter strW = (StringWriter)covDelegate(); 
    Console.WriteLine(strW.ToString()); 
  } 
} 

We have generated StringWriter in the StringWriterMethod() method using the following code:

StringWriter strWriter = new StringWriter(); 
string[] arrString = new string[]{ 
  // Array of string 
}; 
foreach (string str in arrString) 
{ 
  strWriter.Write(str); 
  strWriter.Write(' '); 
} 

Then, we call the following code to write the string to the console:

Console.WriteLine(strW.ToString()); 

If we run the CovarianceStringWriterInvoke() method, the string we have defined in the arrString string array in the StringWriterMethod() method will be displayed, as follows:

Covariance

Now, from our discussion on covariance, we have proved the covariance in delegates. The CovarianceDelegate delegate returning TextWriter can be assigned to the method returning StreamWriter and StringWriter. The following code snippet is taken from several preceding codes to conclude the covariance in delegates:

private delegate TextWriter CovarianceDelegate(); 
CovarianceDelegate covDelegate; 
covDelegate = StreamWriterMethod; 
covDelegate = StringWriterMethod; 

Contravariance

Now, let's continue our discussion on variance in delegates by discussing contravariance. The following is the ContravarianceDelegate delegate declaration, which we can find in the Contravariance.csproj project:

public partial class Program 
{ 
  private delegate void ContravarianceDelegate(StreamWriter sw); 
} 

The preceding delegate is going to be assigned to the following method, which has the TextWriter data type parameter, as follows:

public partial class Program 
{ 
  private static void TextWriterMethod(TextWriter tw) 
  { 
    string[] arrString = new string[]{ 
      "Contravariance", 
      "example", 
      "using", 
      "TextWriter", 
      "object" 
    }; 
    tw = new StreamWriter(Console.OpenStandardOutput()); 
    foreach (string str in arrString) 
    { 
      tw.Write(str); 
      tw.Write(' '); 
    } 
    tw.WriteLine(); 
    Console.SetOut(tw); 
    tw.Flush(); 
  } 
} 

The assignment will be as follows:

public partial class Program 
{ 
  private static void ContravarianceTextWriterInvoke() 
  { 
    ContravarianceDelegate contravDelegate = TextWriterMethod; 
    TextWriter tw = null; 
    Console.WriteLine( 
      "Invoking ContravarianceTextWriterInvoke method:"); 
    contravDelegate((StreamWriter)tw); 
  } 
} 

If we run the ContravarianceTextWriterInvoke() method, the console will display the following output:

Contravariance

From the preceding output, we have successfully assigned a method, taking the TextWriter parameter to the delegate taking the StreamWriter parameter. This happens because StreamWriter is derived from TextWriter. Let's take a look at the following code snippet:

private delegate void ContravarianceDelegate(StreamWriter sw); 
private static void TextWriterMethod(TextWriter tw) 
{ 
  // Implementation 
} 
ContravarianceDelegate contravDelegate = TextWriterMethod; 
TextWriter tw = null; 
contravDelegate((StreamWriter)tw); 

The preceding code snippet is taken from the code we discussed in contravariance. Here, we can see that contravDelegate, a variable typed ContravarianceDelegate, can be assigned to the TextWriterMethod() method even though they both have different signatures. This is because StreamWriter is derived from the TextWriter object. Since the TextWriterMethod() method can work with a TextWriter data type, it will surely be able to work with a StreamWriter data type as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset