21.8 Delegates

In Section 14.3.3, we introduced the concept of a delegate—an object that holds a reference to a method.1 You can call a method through a variable of a delegate type—thus delegating to the referenced method the responsibilty of performing a task. Delegates also allow you to pass methods to and from other methods. We introduced delegates in the context of GUI event handlers, but they’re used in many areas of the .NET Framework. For example, in Chapter 9, we introduced LINQ query syntax. The compiler converts such LINQ queries into calls to extension methods—many of which have delegate parameters. Figure 21.6 declares and uses a delegate type. In Section 21.11, we’ll use delegates in the context of LINQ extension methods.

Fig. 21.6 Using delegates to pass functions as arguments.

Alternate View

 1   // Fig. 21.6: Delegates.cs
 2   // Using delegates to pass functions as arguments.
 3   using System;
 4   using System.Collections.Generic;
 5
 6   class Delegates
 7   {
 8      // delegate for a function that receives an int and returns a bool
 9      public delegate bool NumberPredicate(int number);
10
11      static void Main()
12      {
13         int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
14
15         // create an instance of the NumberPredicate delegate type
16         NumberPredicate evenPredicate = IsEven;
17
18         // call IsEven using a delegate variable
19         Console.WriteLine(
20            $"Call IsEven using a delegate variable: {evenPredicate(4)} ");
21
22         // filter the even numbers using method IsEven
23         List<int> evenNumbers = FilterArray(numbers, evenPredicate);
24
25         // display the result
26         DisplayList("Use IsEven to filter even numbers: ", evenNumbers);
27
28         // filter the odd numbers using method IsOdd
29         List<int> oddNumbers = FilterArray(numbers, IsOdd);
30
31         // display the result
32         DisplayList("Use IsOdd to filter odd numbers: ", oddNumbers);
33
34         // filter numbers greater than 5 using method IsOver5
35         List<int> numbersOver5 = FilterArray(numbers, IsOver5);
36
37         // display the result
38         DisplayList("Use IsOver5 to filter numbers over 5: ", numbersOver5);
39      }
40
41      // select an array's elements that satisfy the predicate
42      private static List<int> FilterArray(int[] intArray,
43         NumberPredicate predicate)
44      {
45         // hold the selected elements
46         var result = new List<int>();
47
48         // iterate over each element in the array
49         foreach (var item in intArray)
50         {
51            // if the element satisfies the predicate
52            if (predicate(item)) // invokes method referenced by predicate
53            {
54               result.Add(item); // add the element to the result
55            }
56         }
57
58         return result; // return the result
59      }
60
61      // determine whether an int is even
62      private static bool IsEven(int number) => number % 2 == 0;
63
64      // determine whether an int is odd
65      private static bool IsOdd(int number) => number % 2 == 1;
66
67      // determine whether an int is greater than 5
68      private static bool IsOver5(int number) => number > 5;
69
70      // display the elements of a List
71      private static void DisplayList(string description, List<int> list)
72      {
73         Console.Write(description); // display the output's description
74
75         // iterate over each element in the List
76         foreach (var item in list)
77         {
78            Console.Write($"{item} "); // print item followed by a space
79         }
80
81         Console.WriteLine(); // add a new line
82      }
83   }

Call IsEven using a delegate variable: True
Use IsEven to filter even numbers: 2 4 6 8 10
Use IsOdd to filter odd numbers: 1 3 5 7 9
Use IsOver5 to filter numbers over 5: 6 7 8 9 10

21.8.1 Declaring a Delegate Type

Line 9 defines a delegate type named NumberPredicate. A variable of this type can store a reference to any method that takes one int argument and returns a bool. A delegate type is declared by preceding a method header with keyword delegate (placed after any access specifiers, such as public or private) and following the method header with a semicolon. A delegate type declaration includes the method header only—the header describes a set of methods with specific parameters and a specific return type.

21.8.2 Declaring a Delegate Variable

Line 16 declares evenPredicate as a NumberPredicate variable and initializes it with a reference to the expression-bodied IsEven method (line 62). Since method IsEven’s signature matches the NumberPredicate delegate’s signature, IsEven can be referenced by a variable of type NumberPredicate. Variable evenPredicate can now be used as an alias for method IsEven. A NumberPredicate variable can hold a reference to any method that receives an int and returns a bool. Lines 19–20 use variable evenPredicate to call method IsEven, then display the result. The method referenced by the delegate is called using the delegate variable’s name in place of the method’s name, as in


evenPredicate(4)

21.8.3 Delegate Parameters

The real power of delegates is in passing method references as arguments to methods, as we do in this example with method FilterArray (lines 42–59). The method takes as arguments

  • an int array and

  • a NumberPredicate that references a method used to filter the array elements.

The method returns a List<int> containing only the ints that satisfy the condition specified by the NumberPredicate. FilterArray returns a List, because we don’t know in advance how many elements will be included in the result.

The foreach statement (lines 49–56) calls the method referenced by the NumberPredicate delegate (line 52) once for each element of the array. If the method call returns true, the element is included in result. The NumberPredicate is guaranteed to return either true or false, because any method referenced by a NumberPredicate must return a bool—as specified by the definition of the NumberPredicate delegate type (line 9). Line 23 passes to FilterArray the int array (numbers) and the NumberPredicate that references the IsEven method (evenPredicate). FilterArray then calls the NumberPredicate delegate on each array element. Line 23 assigns the List returned by FilterArray to variable evenNumbers and line 26 calls method DisplayList (lines 71–82) to display the results.

21.8.4 Passing a Method Name Directly to a Delegate Parameter

Line 29 calls method FilterArray to select the odd numbers in the array. In this case, we pass the method name IsOdd (defined in line 65) as FilterArray’s second argument, rather than creating a NumberPredicate variable. Line 32 displays the results showing only the odd numbers. Line 35 calls method FilterArray to select the numbers greater than 5 in the array, using method IsOver5 (defined in line 68) as FilterArray’s second argument. Line 38 displays the elements that are greater than 5.

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

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