Chapter 2. Walkthrough Delegates

In the previous chapter, we applied delegates in the code we created. When we discussed the concept of functional programming, we applied one of the built-in delegates that C# has. In this chapter, we are going to delve into the delegates that will be used a lot in functional C# programming by discussing the following topics:

  • The definition, syntax, and use of delegates
  • Combining delegates into multicast delegates
  • Using built-in delegates
  • Understanding the variance in delegates

Introducing delegates

A delegate is a data type in C# that encapsulates a method that has particular parameters and return types (signatures). In other words, a delegate will define the parameters and the return type of a method. Delegates are similar to function pointers in C/C++ since both stores the reference to the method with a particular signature. Like a function pointer in C/C++, a delegate keeps a memory address of the method it refers to. The compiler will complain if it refers to a function with a different signature. However, because of the unmanaged nature of the C++ language, one can point functions to arbitrary locations (by casting).

Let's take a look at the following delegate syntax:

[AccessModifier] delegate ReturnType DelegateName([parameters]); 

Here is the explanation for each element of the preceding delegate syntax:

  • AccessModifier: This is the modifier that is used to set the accessibility of the delegate. It can be public, private, internal, or protected. However, we can omit it, and if we do that, the default modifier will be internal.
  • delegate: This is the keyword we need in order to initialize a delegate.
  • ReturnType: This is a returning data type of the method we assign to this delegate.
  • DelegateName: This is the identity of the delegate.
  • parameters: This is the list of parameters that the method we assign to this delegate takes.

By referring to the preceding syntax, we can initialize the delegate, for instance, SingleStringDelegate:

public delegate void SingleStringDelegate(string dataString); 

Since we have the preceding delegate, we can assign a method possessing the same signature to the delegate. The method can be as follows:

private static void AssignData(string dataString) 
{ 
  globalString = dataString; 
} 

Or, the method can be as follows:

private static void WriteToConsole(string dataText) 
{ 
  Console.WriteLine(dataText); 
} 

Since both methods have an identical signature, we can assign them to SingleStringDelegate using the following syntax:

SingleStringDelegate delegate1 = AssignData; 

The preceding syntax is used to assign the AssignData() method to a variable typed SingleStringDelegate, and for the WriteToConsole() method, we can use the following syntax:

SingleStringDelegate delegate2 = WriteToConsole; 

Note

It is common to name a delegate type ending with the word Delegate—for example, SingleStringDelegate—in order to be able to distinguish the delegate name and the method name. However, it is not mandatory and we can omit this.

Simple delegates

For further discussion on delegates, let's take a look at the following method, which we can find at SimpleDelegates.csproj:

public partial class Program 
{ 
  static int Rectangle(int a, int b) 
  { 
    return a * b; 
  } 
} 

The Rectangle() method in the preceding code can be assigned to the delegate variable given in the following code:

public partial class Program 
{ 
  private delegate int AreaCalculatorDelegate(int x, int y); 
} 

The following method can also be assigned to the AreaCalculatorDelegate delegate because the signature of the method is what the delegate type expects:

public partial class Program 
{ 
  static int Square(int x, int y) 
  { 
    return x * y; 
  } 
} 

To assign a method to a delegate, we just need to create a variable of the delegate data type which has signature compatibility with the method to be assigned. The following is the Main() method, which will create the delegate variable and invoke the method:

public partial class Program 
{ 
  static void Main(string[] args) 
  { 
    AreaCalculatorDelegate rect = Rectangle; 
    AreaCalculatorDelegate sqr = Square; 
    int i = rect(1, 2); 
    int j = sqr(2, 3); 
    Console.WriteLine("i = " + i); 
    Console.WriteLine("j = " + j); 
  } 
} 

From the preceding code, we create two variables named rect and sqr whose type is AreaCalculatorDelegate. Here is the code snippet:

AreaCalculatorDelegate rect = Rectangle; 
AreaCalculatorDelegate sqr = Square; 

Since we have assigned the rect and sqr variables to the Rectangle() and Square() methods, we can invoke these methods using the delegate variable. Let's take a look at the following code snippet:

int i = rect(1, 2); 
int j = sqr(2, 3); 

We assign variable i and j with the result of rect() and sqr(). Although both of them are variable names, they refer to the method address location. One invokes a method referred by these variables to execute the logic contained. We are effectively executing the two Console.WriteLine() methods to produce the following output:

Simple delegates

It is now clear to the reader that why we display the output shown in the preceding screenshot. The rect and sqr variables now store the reference to Rectangle() and Square() methods respectively. We are effectively calling the Rectangle() method while invoking the rect delegate and Square() method, all the while invoking the sqr delegate.

Multicast delegates

We have just discussed a simple delegate where we assign a particular method to a delegate variable. We can call it a unicast delegate. However, the delegates can actually invoke multiple methods using one variable. For this purpose, we can call it a multicast delegate. In the case of multicast delegate, it is like a list of delegates stored inside an internal list. When we invoke a multicast delegate, the delegates in the list are synchronously called in the correct order. There are several ways to create a multicast delegate. The two we will discuss in detail are the Delegate.Combine() and Delegate.Remove() methods and the += and -= (increment and decrement) operators.

Using the Delegate.Combine() and Delegate.Remove() methods

Let's first examine the following code, creating a multicast delegate using the Delegate.Combine() method. Suppose we have a delegate named CalculatorDelegate, as follows, which we can find at CombineDelegates.csproj:

public partial class Program 
{ 
  private delegate void CalculatorDelegate(int a, int b); 
} 

Then, we have the following four methods that have the same signature as the CalculatorDelegate signature:

public partial class Program 
{ 
  private static void Add(int x, int y) 
  { 
    Console.WriteLine( 
      "{0} + {1} = {2}", 
      x, 
      y, 
      x + y); 
  } 
  private static void Subtract(int x, int y) 
  { 
    Console.WriteLine( 
      "{0} - {1} = {2}", 
      x, 
      y, 
      x - y); 
  } 
  private static void Multiply(int x, int y) 
  { 
    Console.WriteLine( 
      "{0} * {1} = {2}", 
      x, 
      y, 
      x * y); 
  } 
  private static void Division(int x, int y) 
  { 
    Console.WriteLine( 
      "{0} / {1} = {2}", 
      x, 
      y, 
      x / y); 
  } 
} 

There are four methods, and they are Add(), Subtract(), Multiply(), and Division(). We are going to cast these methods in a single variable-typed delegate. Now, take a look at the following CombineDelegate() method to achieve this goal:

public partial class Program 
{ 
  private static void CombineDelegate() 
  { 
    CalculatorDelegate calcMultiples = 
      (CalculatorDelegate)Delegate.Combine( 
      new CalculatorDelegate[] { 
      Add, 
      Subtract, 
      Multiply, 
      Division }); 
    Delegate[] calcList = calcMultiples.GetInvocationList(); 
    Console.WriteLine( 
      "Total delegates in calcMultiples: {0}", 
      calcList.Length); 
    calcMultiples(6, 3); 
  } 
} 

If we run this method, the following output will be displayed:

Using the Delegate.Combine() and Delegate.Remove() methods

We have successfully invoked four methods by calling a single delegate. The delegate we called in the preceding code is in the following code snippet:

calcMultiples(6, 3); 

Actually calcMultiples delegate has stored four delegates variables internally, corresponding to each of the method which we combined. Thanks to the Delegate.Combine() method, we can combine the delegates using the following syntax:

CalculatorDelegate calcMultiples = 
  (CalculatorDelegate)Delegate.Combine( 
    new CalculatorDelegate[] { 
      Add, 
      Subtract, 
      Multiply, 
      Division }); 

We can also create the array of delegates by calling GetInvocationList() from the delegate variable. By retrieving the delegate array, we can iterate over the array like we do for ordinary arrays. We can retrieve the Length property to count how many delegates are there in the invocation list.

In multicast delegates, we are able to combine as well remove delegates from the invocation list. Let's take a look at the following RemoveDelegate() method:

public partial class Program 
{ 
  private static void RemoveDelegate() 
  { 
    CalculatorDelegate addDel = Add; 
    CalculatorDelegate subDel = Subtract; 
    CalculatorDelegate mulDel = Multiply; 
    CalculatorDelegate divDel = Division; 
    CalculatorDelegate calcDelegates1 = 
      (CalculatorDelegate)Delegate.Combine( 
      addDel, 
      subDel); 
    CalculatorDelegate calcDelegates2 = 
      (CalculatorDelegate)Delegate.Combine( 
      calcDelegates1, 
      mulDel); 
    CalculatorDelegate calcDelegates3 = 
      (CalculatorDelegate)Delegate.Combine( 
      calcDelegates2, 
      divDel); 
    Console.WriteLine( 
      "Total delegates in calcDelegates3: {0}", 
      calcDelegates3.GetInvocationList().Length); 
    calcDelegates3(6, 3); 
    CalculatorDelegate calcDelegates4 = 
      (CalculatorDelegate)Delegate.Remove( 
      calcDelegates3, 
      mulDel); 
    Console.WriteLine( 
      "Total delegates in calcDelegates4: {0}", 
      calcDelegates4.GetInvocationList().Length); 
    calcDelegates4(6, 3); 
  } 
} 

If we run the preceding method, the following output will be displayed in the console:

Using the Delegate.Combine() and Delegate.Remove() methods

Similar to the CombineDelegate() method, we combine the four methods into a single variable-typed delegate in the RemoveDelegate() method. The calcDelegates3 delegate is the delegate that keeps the four methods. Indeed, when we invoke calcDelegates3, it calls the four methods in a proper order. Next, in the RemoveDelegate() method, we invoke the Delegate.Remove() method in order to remove the selected delegate in the invocation list. Based on the preceding code, the syntax is as follows:

CalculatorDelegate calcDelegates4 = 
  (CalculatorDelegate)Delegate.Remove( 
  calcDelegates3, 
  mulDel); 

The preceding code snippet is used to remove the mulDel delegate variable from the invocation list. As we can see in the preceding figure displaying the output of the RemoveDelegate() invocation, the Multiply() method is no longer invoked right after it's removed from invocation list.

An invocation list associated with a delegate can contain duplicate entries. This means that we can add the same method to the invocation list more than once. Now let's try to insert the duplicate entries into the invocation list by adding the DuplicateEntries() method to the project, as follows:

public partial class Program 
{ 
  private static void DuplicateEntries() 
  { 
    CalculatorDelegate addDel = Add; 
    CalculatorDelegate subDel = Subtract; 
    CalculatorDelegate mulDel = Multiply; 
    CalculatorDelegate duplicateDelegates1 = 
      (CalculatorDelegate)Delegate.Combine( 
      addDel, 
      subDel); 
    CalculatorDelegate duplicateDelegates2 = 
      (CalculatorDelegate)Delegate.Combine( 
      duplicateDelegates1, 
      mulDel); 
    CalculatorDelegate duplicateDelegates3 = 
      (CalculatorDelegate)Delegate.Combine( 
      duplicateDelegates2, 
      subDel); 
    CalculatorDelegate duplicateDelegates4 = 
      (CalculatorDelegate)Delegate.Combine( 
      duplicateDelegates3, 
      addDel); 
    Console.WriteLine( 
      "Total delegates in duplicateDelegates4: {0}", 
      duplicateDelegates4.GetInvocationList().Length); 
      duplicateDelegates4(6, 3); 
  } 
} 

Now let's run the DuplicateEntries() method, and the console will show the following output:

Using the Delegate.Combine() and Delegate.Remove() methods

By examining the preceding code, we can see that the duplicateDelegates2 variable contains three invocation methods, which are addDel, subDel, and mulDel. Look at the following code snippet for more details:

CalculatorDelegate duplicateDelegates1 = 
  (CalculatorDelegate)Delegate.Combine( 
  addDel, 
  subDel); 
CalculatorDelegate duplicateDelegates2 = 
  (CalculatorDelegate)Delegate.Combine( 
  duplicateDelegates1, 
  mulDel); 

Again, we add subDel and addDel to the invocation list like we do in the following code snippet:

CalculatorDelegate duplicateDelegates3 = 
  (CalculatorDelegate)Delegate.Combine( 
  duplicateDelegates2, 
  subDel); 
CalculatorDelegate duplicateDelegates4 = 
  (CalculatorDelegate)Delegate.Combine( 
  duplicateDelegates3, 
  addDel); 

Now, the invocation list of duplicateDelegates4 contains two duplicate methods. However, when we invoke the DuplicateEntries() method, addDel and subDel are invoked twice and the invocation order is just like the order in which we add the delegate to the invocation list.

Note

The Delegate.Combine() and Delegate.Remove() static methods will return the Delegate data type instead of the instance of Delegate itself. As a result, casting the return of both methods to the expected instance delegate is required when using them.

Using += and -= operators

It's quite easy to create multicast delegates using += and -= operators since that will be like treating any data types in C#. We can also use the + and - operators to add and remove delegates in an invocation list. Here is the sample code we can find at AddSubtractDelegates.csproj in order to combine delegates and remove select delegates from the invocation list using the operator:

public partial class Program 
{ 
  private static void AddSubtractDelegate() 
  { 
    CalculatorDelegate addDel = Add; 
    CalculatorDelegate subDel = Subtract; 
    CalculatorDelegate mulDel = Multiply; 
    CalculatorDelegate divDel = Division; 
    CalculatorDelegate multiDel = addDel + subDel; 
    multiDel += mulDel; 
    multiDel += divDel; 
    Console.WriteLine( 
      "Invoking multiDel delegate (four methods):"); 
    multiDel(8, 2); 
    multiDel = multiDel - subDel; 
    multiDel -= mulDel; 
    Console.WriteLine( 
      "Invoking multiDel delegate (after subtraction):"); 
    multiDel(8, 2); 
  } 
} 

We also have the four methods that we use in the preceding project, CombineDelegates.csprojAdd(), Subtract(), Multiply(), and Division(). We will get the following output if we run the AddSubtractDelegate() method:

Using += and -= operators

In the starting lines of the AddSubtractDelegate() method, we create four variables typed CalculatorDelegate for each of the four methods we have, just like we did in the previous project. We then create one more variable named multiDel in order to generate the multicast delegate. Here, we can see that we add the delegate to the multicast delegate variable using the operator only, in which we use the + and += operators. Let's take a look at the following code snippet:

CalculatorDelegate multiDel = addDel + subDel; 
multiDel += mulDel; 
multiDel += divDel; 
Console.WriteLine( 
  "Invoking multiDel delegate (four methods):"); 
multiDel(8, 2); 

From the preceding code snippet, after combining all four delegates into the multiDel delegate, we call the multiDel delegate, and what we get based on the output console display is the program to invoke the four methods in a proper order. The four methods are Add(), Subtract(), Multiply(), and Division().

To remove the delegate from the invocation list, we use the - and -= operators in the preceding code. Let's take a look at the following code snippet to examine what we have to do in order to remove the delegate:

multiDel = multiDel - subDel; 
multiDel -= mulDel; 
Console.WriteLine( 
  "Invoking multiDel delegate (after subtraction):"); 
multiDel(8, 2); 

Since we have removed the subDel and mulDel delegates from the invocation list, the program only calls two methods, the Add() and Division() methods, when we invoke the mulDel delegate. This proves that we have successfully removed the delegate from the invocation list using the - and -= operators.

Tip

Using += and -= to assign a multicast delegate doesn't fit the functional programming approach since it breaks the immutability concept. However, we can still use the + and - operators to add a delegate to the invocation list and remove the delegate from the invocation list consecutively in a functional approach.

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

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