Delegate Data TypesTo increase the flexibility (and reduce code duplication), you can pass in the comparison method as a parameter to the BubbleSort() method. Moreover, in order to pass a method as a parameter, there needs to be a data type that can represent that method-in other words, a delegate. Listing 12.3 includes a modification to the BubbleSort() method that takes a delegate parameter. In this case, the delegate data type is ComparisonHandler.Listing 12.3: BubbleSort() Method with Delegate Parameterclass DelegateSample{// ...{int i;int j;int temp;if(items==null){return;}public static void BubbleSort(int[] items, ComparisonHandler comparisonMethod)
if(comparisonMethod == null){throw new ArgumentNullException("comparisonMethod");}for (i = items.Length - 1; i >= 0; i--){for (j = 1; j <= i; j++){{temp = items[j - 1];items[j - 1] = items[j];items[j] = temp;}}}}// ...}ComparisonHandler is a data type that represents a method for comparing two integers. Within the BubbleSort() method you then use the instance of the ComparisonHandler, called comparisonMethod, inside the conditional expression. Since comparisonMethod represents a method, the syntax to invoke the method is identical to calling the method directly. In this case, comparisonMethod takes two integer parameters and returns a Boolean value that indicates whether the first integer is greater than the second one.Perhaps more noteworthy than the particular algorithm, the ComparisonHandler delegate is strongly typed to return a bool and to accept only two integer parameters. Just as with any other method, the call to a delegate is strongly typed, and if the data types do not match up, then the C# compiler reports an error. Let us consider how the delegate works internally.Delegate InternalsC# defines all delegates, including ComparisonHandler, as derived indirectly from System.Delegate, as shown in Figure 12.1.1if (comparisonMethod(items[j - 1], items[j]))1. The C# standard doesn't specify the delegate implementation's class hierarchy. .NET's implementation, however, does derive indirectly from System.Delegate.Figure 12.1: Delegate Types Object ModelThe first property is of type System.Reflection.MethodInfo, which I cover in Chapter 17. MethodInfo describes the signature of a particular method, including its name, parameters, and return type. In addition to MethodInfo, a delegate also needs the instance of the object containing the method to invoke. This is the purpose of the second property, Target. In the case of a static method, Target corresponds to the type itself. The purpose of the MulticastDelegate class is the topic of the next chapter.Defining a Delegate TypeYou saw how to define a method that uses a delegate, and you learned how to invoke a call to the delegate simply by treating the delegate variable as a method. However, you have yet to learn how to declare a delegate data type. For example, you have not learned how to define Comparison- Handler such that it requires two integer parameters and returns a bool. Although all delegate data types derive indirectly from System.Delegate, the C# compiler does not allow you to define a class that derives directly or indirectly (via System.MulticastDelegate) from System.Delegate. Listing 12.4, therefore, is not valid.Listing 12.4: System.Delegate Cannot Explicitly Be a Base Class // ERROR: 'ComparisonHandler' cannot // inherit from special class 'System.Delegate' public class ComparisonHandler : System.Delegate { // ... }In its place, C# uses the delegate keyword. This keyword causes the compiler to generate a class similar to the one shown in Listing 12.4. Listing 12.5 shows the syntax for declaring a delegate data type.Listing 12.5: Declaring a Delegate Data Type public delegate bool ComparisonHandler( int first, int second);In other words, the delegate keyword is shorthand for declaring a reference type derived ultimately from System.Delegate. In fact, if the delegate declaration appeared within another class, then the delegate type, ComparisonHandler, would be a nested type (see Listing 12.6).Listing 12.6: Declaring a Nested Delegate Data Type class DelegateSample { public delegate bool ComparisonHandler( int first, int second); }In this case, the data type would be DelegateSample.ComparisonHandler because it is defined as a nested type within DelegateSample.Instantiating a DelegateIn this final step of implementing the BubbleSort() method with a delegate, you will learn how to call the method and pass a delegate instance-specifically, an instance of type ComparisonHandler. To instantiate a delegate, you need a method that corresponds to the signature of the delegate type itself. In the case of ComparisonHandler, that method takes two integers and returns a bool. The name of the method is not significant. Listing 12.7 shows the code for a greater-than method.Listing 12.7: Declaring a ComparisonHandler-Compatible Method public delegate bool ComparisonHandler( int first, int second); class DelegateSample { public static void BubbleSort( int[] items, ComparisonHandler comparisonMethod) { // ... } // ... }With this method defined, you can call BubbleSort() and pass the delegate instance that contains this method. Beginning with C# 2.0, you simply specify the name of the delegate method (see Listing 12.8).Listing 12.8: Passing a Delegate Instance as a Parameter in C# 2.0public delegate bool ComparisonHandler (int first, int second);class DelegateSample{public static void BubbleSort(int[] items, ComparisonHandler comparisonMethod){// ...}{return first > second;}static void Main(){int[] items = new int[100];public static bool GreaterThan(int first, int second){return first > second;}public static bool GreaterThan(int first, int second)
Random random = new Random();for (int i = 0; i < items.Length; i++){items[i] = random.Next(int.MinValue, int.MaxValue);}for (int i = 0; i < items.Length; i++){Console.WriteLine(items[i]);}}}Note that the ComparisonHandler delegate is a reference type, but you do not necessarily use new to instantiate it. The facility to pass the name rather than explicit instantiation is delegate inference, a new syntax beginning with C# 2.0. With this syntax, the compiler uses the method name to look up the method signature and verify that it matches the method's parameter type.