Built-in delegates

In C#, not only are we able to declare a delegate, but we are also able to use the built-in delegate from the C# standard library. This built-in delegate also applies to the generic data type, so let's discuss the generic delegate prior to discussing the built-in delegate.

Generic delegates

A delegate type can use a generic type as its parameter. Using the generic type, we can put off the specification of one or more types in parameters or return values until the delegate is initialized into a variable. In other words, we do not specify the data types of the delegate's parameters and return values when we define a delegate type. To discuss this in more detail, let's take a look at the following code, which we can find at GenericDelegates.csproj:

public partial class Program 
{ 
  private delegate T FormulaDelegate<T>(T a, T b); 
} 

We have a delegate name, FormulaDelegate, using the generic data type. As we can see, there is a T symbol, which represents the data type we will define when declaring the variable typed FormulaDelegate. We continue by adding the following two methods that have completely different signatures:

public partial class Program 
{ 
  private static int AddInt(int x, int y) 
  { 
    return x + y; 
  } 
  private static double AddDouble(double x, double y) 
  { 
    return x + y; 
  } 
} 

Now let's take a look at the following code in order to explain how we declare the variable-typed delegate and invoke the method from the delegate:

public partial class Program 
{ 
  private static void GenericDelegateInvoke() 
  { 
    FormulaDelegate<int> intAddition = AddInt; 
    FormulaDelegate<double> doubleAddition = AddDouble; 
    Console.WriteLine("Invoking intAddition(2, 3)"); 
    Console.WriteLine( 
      "Result = {0}", 
      intAddition(2, 3)); 
    Console.WriteLine("Invoking doubleAddition(2.2, 3.5)"); 
    Console.WriteLine( 
      "Result = {0}", 
      doubleAddition(2.2, 3.5)); 
  } 
} 

The following result will be displayed in the console when we run the GenericDelegateInvoke() method:

Generic delegates

From the preceding code, we can declare two methods that have different signature using only one delegate type. The intAddition delegate refers to the AddInt() method, which applies the int data type in its parameters and return value, while the doubleAddition delegate refers to the AddDouble() method, which applies the double data type in its parameters and return value. However, in order for the delegate to know the data type of the method it refers, we have to define the data type in angular brackets (<>) when we initialize the delegate. The following code snippet is the delegate initialization that uses the generic data type (symbolized by the angular brackets):

FormulaDelegate<int> intAddition = AddInt; 
FormulaDelegate<double> doubleAddition = AddDouble; 

Because we have defined the data type, the delegate can match the data type of the method it refers. That's why, from the output console, we can invoke the two methods that have different signatures.

We have successfully used a generic type for delegates, applying one generic template. The following code, which we can find at MultiTemplateDelegates.csproj, shows us that the delegate can also apply the multigeneric template in one delegate declaration:

public partial class Program 
{ 
  private delegate void AdditionDelegate<T1, T2>( 
    T1 value1, T2 value2); 
} 

The preceding code will create a new delegate named AdditionDelegate, which has two parameters with two different data types. T1 and T2 represent the data type that will be defined in the variable-typed delegate declaration. Now, let's create two methods that have different signatures, as follows:

public partial class Program 
{ 
  private static void AddIntDouble(int x, double y) 
  { 
    Console.WriteLine( 
      "int {0} + double {1} = {2}", 
      x, 
      y, 
      x + y); 
  } 
  private static void AddFloatDouble(float x, double y) 
  { 
    Console.WriteLine( 
      "float {0} + double {1} = {2}", 
      x, 
      y, 
      x + y); 
  } 
} 

To refer the AdditionDelegate delegate to the AddIntDouble() and AddFloatDouble() methods and invoke the delegate, we can create the VoidDelegateInvoke() method, as follows:

public partial class Program 
{ 
  private static void VoidDelegateInvoke() 
  { 
    AdditionDelegate<int, double> intDoubleAdd = 
      AddIntDouble; 
    AdditionDelegate<float, double> floatDoubleAdd = 
      AddFloatDouble; 
    Console.WriteLine("Invoking intDoubleAdd delegate"); 
    intDoubleAdd(1, 2.5); 
    Console.WriteLine("Invoking floatDoubleAdd delegate"); 
    floatDoubleAdd((float)1.2, 4.3); 
  } 
} 

If we run the VoidDelegateInvoke() method, we will see the following output on our console:

Generic delegates

From the preceding console output, it can be seen that we have successfully invoked the intDoubleAdd and floatDoubleAdd delegates although they have different method signatures. This is possible since we apply the T1 and T2 template in the AdditionDelegate delegate.

Let's try to create the multitemplate delegate again, but this time, we use the method that has a return value. The declaration of the delegate will be as follows:

public partial class Program 
{ 
  private delegate TResult AddAndConvert<T1, T2, TResult>( 
    T1 digit1, T2 digit2); 
} 

Then, we add the two methods AddIntDoubleConvert() and AddFloatDoubleConvert() to our project:

public partial class Program 
{ 
  private static float AddIntDoubleConvert(int x, double y) 
  { 
    float result = (float)(x + y); 
    Console.WriteLine( 
      "(int) {0} + (double) {1} = (float) {2}", 
      x, 
      y, 
      result); 
    return result; 
  } 
  private static int AddFloatDoubleConvert(float x, double y) 
  { 
    int result = (int)(x + y); 
    Console.WriteLine( 
      "(float) {0} + (double) {1} = (int) {2}", 
      x, 
      y, 
      result); 
    return result; 
  } 
} 

In order to use the AddAndConvert delegate, we can create the ReturnValueDelegateInvoke() method, as follows:

public partial class Program 
{ 
  private static void ReturnValueDelegateInvoke() 
  { 
    AddAndConvert<int, double, float>
        intDoubleAddConvertToFloat = AddIntDoubleConvert; 
    AddAndConvert<float, double, int>
        floatDoubleAddConvertToInt = AddFloatDoubleConvert; 
    Console.WriteLine("Invoking intDoubleAddConvertToFloat delegate"); 
    float f = intDoubleAddConvertToFloat(5, 3.9); 
    Console.WriteLine("Invoking floatDoubleAddConvertToInt delegate"); 
    int i = floatDoubleAddConvertToInt((float)4.3, 2.1); 
  } 
} 

When we invoke the ReturnValueDelegateInvoke() method, we get the following output:

Generic delegates

Again, we successfully invoke the two different signature methods using a multitemplate generic type.

The Action and Func delegates

Let's go back to the following delegate declaration we discussed earlier in the chapter:

public partial class Program 
{ 
  private delegate void AdditionDelegate<T1, T2>( 
    T1 value1, T2 value2); 
} 

C# has a built-in delegate that can take a maximum of 16 parameters and return void. It is called the Action delegate. In other words, the Action delegate will point to a method that return nothing and takes zero, one, or more input parameters. Due to the existence of the Action delegate, we no longer need to declare a delegate, and we can immediately assign any method to the delegate. We can modify the preceding MultiTemplateDelegates.csproj project and remove the AdditionDelegate delegate since we will now use the Action delegate. Then, the ActionDelegateInvoke() method in MultiTemplateDelegates.csproj will be modified to become ActionDelegateInvoke() with the following implementation:

public partial class Program 
{ 
  private static void ActionDelegateInvoke() 
  { 
    Action<int, double> intDoubleAddAction = 
      AddIntDouble; 
    Action<float, double> floatDoubleAddAction = 
      AddFloatDouble; 
    Console.WriteLine( 
      "Invoking intDoubleAddAction delegate"); 
    intDoubleAddAction(1, 2.5); 
    Console.WriteLine( 
      "Invoking floatDoubleAddAction delegate"); 
    floatDoubleAddAction((float)1.2, 4.3); 
  } 
} 

We can find the preceding code in the ActionFuncDelegates.csproj project. As we can see, now we apply the Action delegate to replace the AdditionDelegate delegate in the MultiTemplateDelegates.csproj project, as follows:

Action<int, double> intDoubleAddAction = 
  AddIntDouble; 
Action<float, double> floatDoubleAddAction = 
  AddFloatDouble; 

C# has another built-in delegate that has a return value by taking a maximum of 16 parameters. They are Func delegates. Let's go back to the MultiTemplateDelegates.csproj project and find the following delegate:

public partial class Program 
{ 
  private delegate TResult AddAndConvert<T1, T2, TResult>( 
    T1 digit1, T2 digit2); 
} 

We can remove the preceding delegate since it matches the declaration of the Func delegate. So, we can modify the ReturnValueDelegateInvoke() method in the MultiTemplateDelegates.csproj project for it to become the FuncDelegateInvoke() method with the following implementation:

public partial class Program 
{ 
  private static void FuncDelegateInvoke() 
  { 
    Func<int, double, float> 
       intDoubleAddConvertToFloatFunc = 
          AddIntDoubleConvert; 
    Func<float, double, int> 
       floatDoubleAddConvertToIntFunc = 
          AddFloatDoubleConvert; 
    Console.WriteLine( 
      "Invoking intDoubleAddConvertToFloatFunc delegate"); 
    float f = intDoubleAddConvertToFloatFunc(5, 3.9); 
    Console.WriteLine( 
      "Invoking floatDoubleAddConvertToIntFunc delegate"); 
    int i = floatDoubleAddConvertToIntFunc((float)4.3, 2.1); 
  } 
} 

Now, we  no longer need the AddAndConvert delegate anymore since we have applied the Func delegate, as follows:

Func<int, double, float> 
  intDoubleAddConvertToFloatFunc = AddIntDoubleConvert; 
Func<float, double, int> 
  floatDoubleAddConvertToIntFunc = AddFloatDoubleConvert; 

Using the Action and Func built-in delegates, the code becomes shorter and the definition of the delegate becomes easier and quicker.

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

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