Introduction
Delegates make it possible to programmatically change method calls and plug new codes into existing classes. For these, you need to know what are the method matches delegate's signature and its return type. Delegates are similar to C++ function pointers, but delegates are type-safe and object-oriented. Methods don't need to match the delegate signature exactly.
What is Covariance and Contravariance in Delegates?
Covariance and Contravariance provide a kind of flexibility when matching the method signature with the delegate type that is defined.
Covariance permits a method to have a more derived return type than what is defined when the delegate is defined. Contravariance permits a method to have less derived parameter types than in the delegate type.
A delegate is a type; that encapsulates a method; see the example below.
using System;
// Define the Delegate
public delegate void TestDelegate(string Message);
public class Program
{
// Creating a method with string argument
public static void DisplayMessage(string Message)
{
Console.WriteLine(Message);
Console.ReadKey();
}
public static void Main(string[] args)
{
// Instantiate the delegate and encapsulate the method "DisplayMessage"
TestDelegate Test = DisplayMessage;
// Call the delegate
Test("This is my delegate method");
}
}
This is a very simple example, but delegates can be used in advanced ways rather than this. The instantiated delegate is an object; it can be passed as a parameter or assigned to a property. When a delegate is used in this way, the code used by the delegate does not need any knowledge of the method being used. This is similar functionality provided by interface encapsulation.
What is Multicasting?
In a delegate, you can have more than one method to invoke; you can assign a method to a delegate using "=," and you can add method a to a delegate where it has assigned a method to invoke by using "+" if you want you can remove an assigned method from a delegate where it has more methods to invoke by using "-". This is called multicasting.
Example
namespace Delegates_Test2
{
delegate void Del();
class Simple
{
public void MethodA()
{
System.Console.WriteLine("Message from the method A.");
}
public static void MethodB()
{
System.Console.WriteLine("Message from the method B.");
}
}
class Program
{
static void Main(string[] args)
{
Simple sc = new Simple();
// Map delegate to the method: A
Del d = sc.MethodA;
// Map method: B with multicasting
d += Simple.MethodB;
d(); // Both methods are executed here
Console.ReadKey();
}
}
}
Generic Delegates
The delegate can define its own generic type parameters.
public delegate void MyDelegate<T>(T item);
Example
namespace Generic_Delegate
{
// Generic Delegate defined here with a return value of T and two T arguments
public delegate T Calculator<T>(T arg1, T arg2);
class MathUtility
{
// Generic method to execute with two generic argument values and a generic method
public static void Calculate<T>(T[] values1, T[] values2, Calculator<T> calculator)
{
for (int i = 0; i < values1.Length; i++)
{
values1[i] = calculator(values1[i], values2[i]); // Execute the T method with two T arguments
// Assign results to the first parameter
}
}
}
}
namespace Generic_Delegate
{
class Program
{
// My method to find square value
static int FindSquareValue(int x, int y)
{
// Your method's implementation
return x * y; // Your method is defined to return the result
}
// My Method to find division with the first value by the second value
static double FindDivision(double a, double b)
{
// Your method's implementation
return a / b; // Your method is defined to return the result
}
static void Main(string[] args)
{
// To get Squares
int[] height = { 10, 5, 8 };
int[] width = { 4, 3, 6 };
MathUtility.Calculate(height, width, FindSquareValue); // Dynamically hook with FindSquare method
Console.WriteLine("Display Square Values...");
foreach (int i in height)
{
Console.WriteLine(i.ToString());
}
// To get Divisions
double[] val1 = { 10, 80, 60 };
double[] val2 = { 8, 3, 4 };
MathUtility.Calculate(val1, val2, FindDivision);
Console.WriteLine("Display Division values...");
foreach (double d in val1)
{
Console.WriteLine(d.ToString());
}
Console.ReadKey();
}
}
}