© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
G. ByrneTarget C#https://doi.org/10.1007/978-1-4842-8619-7_21

21. Delegates

Gerard Byrne1  
(1)
Belfast, Ireland
 

Concept of Delegates

In the last chapter, we learned about enumerations and saw how they allow us to assign meaningful names to constants. In their basic form, the first name will represent the value 0 and each subsequent name will have a value of one more, but it is possible to assign different values to the names and thereby change the default values. Enumerations, we learned, help make code more readable, and we can use methods of the Enum class to get the name or value of an item or items in the enumeration. Finally, we also saw that an enumeration is a “special” data type, which is defined by the developer.

In this chapter we will look at delegates, which are also a type, but they are a reference type that holds a reference to a method.

The Microsoft site explains delegates as follows:

A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.

Put in more simple terms, we should think of a delegate as being a type that is used to represent a method with a return type and method signature. We read in Chapter 14 with interface methods , we wrote them as a line of code showing the return type followed by the method signature. Examples of method signatures we looked at are
  • VatCalculation(double itemPrice)

  • CalculateTax(double itemPrice, int quantity)

  • CalculateTax(int quantity, double itemPrice)

When we added the return type in front of the method signature, we had the interface method:
  • double VatCalculation(double itemPrice)

  • void CalculateTax(double itemPrice, int quantity)

  • double CalculateTax(int quantity, double itemPrice)

Now, also thinking back Chapter 12, we saw that we could pass arguments to a method, and the method accepted the arguments as its parameters. The arguments and parameters are types, for example, an int or a string or a float. However, we never read about passing a method as an argument to another method, which would accept the method as its parameter. Well, this is where we could use a delegate, to pass a method to another method. The Microsoft site also says

Delegates are used to pass methods as

arguments to other methods.

The ability to pass methods as arguments makes the delegate a perfect candidate when defining what are referred to as callback methods, as we will see throughout the examples we code. When we develop code that needs to work with a delegate, we will use three stages to achieve this:
  • Declare the delegate with its return type and method signature.

  • Then set a target method for the delegate, having created the instance of the delegate.

  • Then invoke the delegate because it has been defined and points to a method.

When we declare a delegate, it will have a similar format to declaring an abstract method, a return type followed by the method signature. Some examples of delegate declarations are
  • public delegate int VATCalculation(double itemPrice);

  • private delegate int CalculationTax(double itemPrice, int quantity);

  • internal delegate int CalculateTax(int quantity, double itemPrice);

Even just on what we have read so far, we can think of delegates as

  • Having an access modifier of public, private, or internal

  • Allowing methods to be passed as arguments to a method that accepts them as parameters

  • Being used when we wish to use callback methods

But they can also
  • Be joined, chained, so that multiple methods can be called from a single delegate, and this is referred to as multicast delegates

  • Be assigned to any method that matches the delegate’s signature, for example, if the delegate declaration is

delegate double Calculation(int valueOne, double valueTwo);
then either or both of these methods can be assigned to an instance of Calculation
public static double CalculateTax(int quantity, double itemPrice)
{
   return quantity * itemPrice * 0.20;
}
public static double CalculateTotalBeforeVAT(int quantity, double itemPrice)
{
  return quantity * itemPrice;
}

since both methods match the delegate signature.

On the other hand, the method shown in the following cannot be assigned to the Calculation delegate as the method signature is different; it has a double followed by an int rather than an int followed by a double:
public static double calculateTotalBeforeVAT(double
itemPrice, int quantity)
    {
      return quantity * itemPrice;
}

Let’s code some C# and build our programming muscle.

Add a new project to hold the code for this chapter.
  1. 1.

    Right-click the solution CoreCSharp .

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Project.

     
  4. 4.

    Choose Console App from the listed templates that appear.

     
  5. 5.

    Click the Next button.

     
  6. 6.

    Name the project Chapter21 and leave it in the same location.

     
  7. 7.

    Click the Next button.

     
  8. 8.

    Choose the framework to be used, which in our projects will be .NET 6.0 or higher.

     
  9. 9.

    Click the Create button.

     
Now we should see the Chapter21 project within the solution called CoreCSharp.
  1. 10.

    Right-click the Chapter21 project in the Solution Explorer panel.

     
  2. 11.

    Click the Set as Startup Project option.

     

Notice how the Chapter21 project name has been made to have bold text, indicating that it is the new startup project and that it is the Program.cs file within it that will be executed when we run the debugging.

Amend the name of the Program.cs file, remembering the coding principle of “self-documenting ” code.
  1. 12.

    Right-click the Program.cs file in the Solution Explorer window.

     
  2. 13.

    Choose Rename.

     
  3. 14.

    Change the name to Delegates.cs.

     
  4. 15.

    Press the Enter key.

     

Single Delegate

Looking back to Chapter 13 on classes , we had two methods as shown in the following:
/******************* METHOD TWELVE ******************/
public double AccumulateClaimAmount(double
claimAmountPassedIn, double totalOfAllClaims)
{
   totalOfAllClaims += claimAmountPassedIn;
   return totalOfAllClaims;
}// End of method AccumulateClaimAmount()
/******************* METHOD THIRTEEN ******************/
public double DetermineVATAmount(double totalValueOfClaimsPassedIn, double vatAmount)
{
   vatAmount = totalValueOfClaimsPassedIn -
                      (totalValueOfClaimsPassedIn / 1.20);
   return vatAmount;
} // End of method DetermineVATAmount()
}  // End of Chapter13 namespace

We will now use these two methods, in a modified form, to work with delegates.

Declare the Delegate with Its Return Type and Method Signature

We will now declare a delegate, outside the class but inside the namespace, and create a Main() method .
  1. 16.

    Amend the code as in Listing 21-1.

     
namespace Chapter21
{
  /*
  A delegate is a type used to represent a method with a
  return type and method signature.
  Here the delegate is called Calculation and the return type
  is double and the two parameters are double
  */
  delegate double Calculation(double itemPrice, double vatAmount);
  internal class Delegates
  {
    static void Main(string[] args)
    {
    } // End of Main() method
  } // End of Delegates class
} // End of Chapter21 namespace
Listing 21-1

Declare a delegate

Instantiate the Delegate and Set Its Target Method

We will now add the “old” method 13 code with amendments, outside the Main() but inside the class , that will become the target for the delegate.
  1. 17.

    Amend the code, as in Listing 21-2.

     
    } // End of Main() method
    /******************* METHOD THIRTEEN ******************/
    public static double DetermineVATAmount(double totalValueOfClaimsPassedIn, double vatAmount)
    {
      vatAmount = totalValueOfClaimsPassedIn - (totalValueOfClaimsPassedIn / 1.20);
      Console.WriteLine($"The DetermineVATAmount method is executing using the parameter {totalValueOfClaimsPassedIn} and the output produced is ${vatAmount} which represents the VAT amount ");
      return vatAmount;
    } // End of method DetermineVATAmount()
  } // End of Delegates class
} // End of Chapter21 namespace
Listing 21-2

Add the method that will be the target for the delegate

We will now instantiate the delegate and then set the target method for the delegate instance to be the method we have just added, DetermineVATAmount() , but without using the ().
  1. 18.

    Amend the code as in Listing 21-3.

     
   static void Main(string[] args)
    {
      // Instantiate the delegate
      Calculation myCalculationDelegate;
      /*
      Point the delegate object myCalculationDelegate to
      the method DetermineVATAmount
      We are making a reference to the method
      */
      myCalculationDelegate = DetermineVATAmount;
    } // End of Main() method
Listing 21-3

Instantiate the delegate and set the target method

Invoke the Delegate

We will now invoke the delegate using our delegate instance. The call will be made and the returned value will be assigned to a local variable . We will then display the returned value from within a WriteLine method .
  1. 19.

    Amend the code, as in Listing 21-4.

     
   myCalculationDelegate = DetermineVATAmount;
   /*
   Invoke the delegate - in other words the delegate
   points to the method that accepts a double data type,
   performs its business logic and then returns a double.
   So here we invoke our myCalculationDelegate delegate
   passing it the value 120.00
   */
   Console.WriteLine("Invoking myCalculationDelegate ");
   double vatAmount = myCalculationDelegate(120.00, 0.00);
   Console.WriteLine($"Invoked delegate returned VAT of ${vatAmount}");
} // End of Main() method
Listing 21-4

Invoke the delegate

  1. 20.

    Click the File menu.

     
  2. 21.

    Choose Save All.

     
  3. 22.

    Click the Debug menu.

     
  4. 23.

    Choose Start Without Debugging.

     
The console output will be as shown in Figure 21-1, and we can see that the method, DetermineVATAmount() , has taken the 120 and the 0.00 and calculated that 20 of this 120 is VAT so the 0.00 was the VAT amount going in and it comes out of the method as 20.

A console window labeled invoking my calculation delegate illustrates how the delegate is invoked and passes 120 to the method to display the output.

Figure 21-1

Output from the casting-style conversion

  1. 24.

    Press the Enter key to close the console window.

     

Very good, we might be thinking. But couldn't we just have called the method directly as we did in Chapter 12 and as we do with methods like the WriteLine() ? Yes, we could. So why use a delegate then? Simply put, because we can use a delegate to pass a method to another method, and we will see this later when we code an example related to filtering policies. This example was just used to get us started with delegates.

Multicast Delegates

At the start of the chapter, we read that delegates can be joined or chained, so that multiple methods can be called from a single delegate, and this is also referred to as multicast delegates. If we think of it another way, we could say a multicast delegate is a delegate that has references to more than one method. This means that when we invoke a multicast delegate, all the methods it is pointing to will be invoked in the order they have been declared.
  1. 25.

    Amend the code, as in Listing 21-5, to add a local variable that the method will use.

     
    static void Main(string[] args)
    {
      // Create the variables we need
      double totalOfAllClaims;
Listing 21-5

Add a local level variable to be used by the new method

  1. 26.

    Amend the code, as in Listing 21-6, to add another method that will become the target for the delegate this was the “old” method 12.

     
    } // End of method DetermineVATAmount()
    /*
    Create the method that will be the target of the
    delegate, this was Method 12
    */
    public static double AccumulateClaimAmount(double claimAmountPassedIn, double totalOfAllClaims)
    {
      totalOfAllClaims += claimAmountPassedIn;
      Console.WriteLine($"The AccumulateClaimAmount method is executing using the parameter {claimAmountPassedIn} and the output produced is ${totalOfAllClaims} which represents the total claims ");
      return totalOfAllClaims;
    } // End of AccumulateClaimAmount method
  } // End of Delegates class
} // End of Chapter21 namespace
Listing 21-6

Add a second method that will become a target for the delegate

Instantiate the Delegate Again and Set the New Instances’ Target Method

  1. 27.

    Amend the code, as in Listing 21-7, to instantiate the delegate again for two more instances calling them myAccumulateDelegate and myMulticastDelegate:

     
    static void Main(string[] args)
    {
      // Instantiate the delegates
      Calculation myCalculationDelegate, myAccumulateDelegate,
      myMulticastDelegate;
Listing 21-7

Instantiate the delegate twice more

  1. 28.

    Amend the code, as in Listing 21-8, to set the target method for the new myAccumulateDelegate instance, that is, point it to the target method.

     
  /*
  Point the delegate object myCalculationDelegate to
  the method DetermineVATAmount
  We are making a reference to the method
  */
  myCalculationDelegate = DetermineVATAmount;
  /*
  Point the delegate object myAccumulateDelegate to the
  method AccumulateClaimAmount
  We are making a reference to the method
  */
  myAccumulateDelegate = AccumulateClaimAmount;
Listing 21-8

Point the myAccumulateDelegate to the new method

Chain the Delegates

We will now chain the delegates, which effectively means we join the delegates to “compound” the action. The chaining creates a multicast delegate.
  1. 29.

    Amend the code, as in Listing 21-9.

     
  /*
  Point the delegate object myAccumulateDelegate to the
  method AccumulateClaimAmount
  We are making a reference to the method
  */
  myAccumulateDelegate = AccumulateClaimAmount;
  /*
  The two delegates, myCalculationDelegate and myAccumulateDelegate, are combined into form myMultipleDelegate
  */
myMulticastDelegate = myCalculationDelegate + myAccumulateDelegate;
Listing 21-9

Chain the delegates

Invoke the Multicast Delegate

  1. 30.

    Amend the code, as in Listing 21-10, to invoke the multicast delegate through our delegate instance, passing it the value 1500, which is the claim amount, and the value 0.00, which will be used in the first method as the vatAmount and in the second method as the totalOfAllClaims .

     
      Console.WriteLine("Invoking myCalculationDelegate ");
      double vatAmount = myCalculationDelegate(120.00, 0.00);
      Console.WriteLine($"Invoked delegate returned VAT of ${vatAmount}");
      Console.WriteLine("-------------------------------");
      Console.WriteLine("Invoking myMulticastDelegate:");
      myMulticastDelegate(1500, 0.00);
    } // End of Main() method
Listing 21-10

Invoke the multicast delegate

  1. 31.

    Click the File menu.

     
  2. 32.

    Choose Save All.

     
  3. 33.

    Click the Debug menu.

     
  4. 34.

    Choose Start Without Debugging.

     
The console output will be as shown in Figure 21-2.

A console window in which my accumulate delegate is invoked. The delegate calls the first and second methods in the multicast delegate, and displays the outputs.

Figure 21-2

Multicast delegate invoked

  1. 35.

    Press the Enter key to close the console window.

     

Great, we have an understanding on what a delegate is, how to declare a delegate, how to set the target for the delegate instance, and how to invoke the delegate. We have also coded a multicast delegate that invokes multiple methods. Yes, this example could be improved, but it has helped us understand the concept of multicast delegates.

More Complex Example

Now we will look at a more complex example , which
  • Uses a Policy class .

  • Creates six instances of the Policy class, each of which passes different values to the constructor.

  • Adds the six instances to a list that holds objects of type Policy.

  • Creates a delegate that accepts a Policy.

  • Creates a method that accepts the delegate as one of its parameters and assigns it to a method. The first time we use the method, we pass it the HardwareType method. The second time, it will be the PolicyDueForRenewal method , and lastly it will be the PremiumGreaterThanTwenty method :

  • Inside the method the delegate is invoked and therefore calls the HardwareType() method or the PolicyDueForRenewal() method or the PremiumGreaterThanTwenty() method .

  • When any of the three methods are called, they are passed a Policy.

  • The HardwareType method returns those items in the list that contain the string Laptop, as their PolicyType, while the other methods check for a date or a value.

So, when we asked the question, "Why use a delegate?", this example will show that it allows us to pass a method to a method.

Create a Policy class.
  1. 1.

    Right-click the Chapter21 project in the Solution Explorer panel.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose Class.

     
  4. 4.

    Name the class Policy.cs.

     
  5. 5.

    Amend the Policy.cs class to have members, a constructor, and a property for each member containing a get and a set accessor for the private members, as in Listing 21-11.

     
namespace Chapter21
{
  internal class Policy
  {
    private int policyNumber;
    private String policyType;
    private double monthlyPremium;
    private String policyEndDate;
    public Policy(int policyNumber, string policyType,
      double monthlyPremium, string policyEndDate)
    {
      this.PolicyNumber = policyNumber;
      this.PolicyType = policyType;
      this.MonthlyPremium = monthlyPremium;
      this.PolicyEndDate = policyEndDate;
    } // End of user constructor
    public int PolicyNumber
    {
      get => policyNumber;
      set => policyNumber = value;
    }
    public string PolicyType
    { get => policyType;
      set => policyType = value;
    }
    public double MonthlyPremium
    { get => monthlyPremium;
      set => monthlyPremium = value;
    }
    public string PolicyEndDate
    { get => policyEndDate;
      set => policyEndDate = value;
    }
  } // End of Policy class
} // End of Chapter21 namespace
Listing 21- 11

Policy class with constructor, members, and property accessors

  1. 6.

    Right-click the Chapter21 project in the Solution Explorer panel.

     
  2. 7.

    Choose Add.

     
  3. 8.

    Choose Class.

     
  4. 9.

    Name the class DelegatePolicy.cs.

     
  5. 10.

    Amend the DelegatePolicy.cs class to have a Main() method and the imports, as in Listing 21-12.

     
using System.Collections.Generic;
namespace Chapter21
{
  internal class DelegatePolicy
  {
    static void Main(string[] args)
    {
    } // End of Main() method
  } // End of DelegatePolicy class
} // End of Chapter21 namespace
Listing 21-12

DelegatePolicy class with the Main() method

We will now, at the class level, declare a delegate with the return type bool, which accepts a Policy as its parameter in the method signature.
  1. 11.

    Amend the DelegatePolicy.cs class, as in Listing 21-13.

     
  internal class DelegatePolicy
  {
    /*
    Declare the delegate we will use.
    In this case we have a return type of bool and the method
    signature states that the method must accept a Policy object
    */
    internal delegate bool FindByDelegate(Policy policy);
    static void Main(string[] args)
Listing 21-13

Declare a delegate that accepts a Policy object

We will now, within the Main() method, create six instances of the Policy class.
  1. 12.

    Amend the class, as in Listing 21-14.

     
  static void Main(string[] args)
  {
    /*
    Create 6 objects of type Policy - Policy is a separate class
    The Policy class has 4 members - policy_number, policyType
    monthlyPremium and the policyEndDate;
    */
    // Laptops
    Policy myPolicyOne = new Policy(123456, "Laptop", 19.99, "31/12/2021");
    Policy myPolicyFive = new Policy(156790, "Laptop", 18.99, "30/12/2021");
    Policy myPolicySix = new Policy(123456, "Laptop", 15.99, "15/12/2021");
    // Need renewed
    Policy myPolicyTwo = new Policy(267890, "Printer", 15.99, "01/11/2021");
    Policy myPolicyThree = new Policy(345908, "Small_Screen", 9.99, "01/10/2021");
    // Monthly premium greater than $20
    Policy myPolicyFour = new Policy(455666, "Large_Screen", 29.99, "01/12/2021");
  } // End of Main() method
Listing 21-14

Create six instances of the Policy class

We will now continue adding code within the Main() method to declare and create a list that will hold the six instances of the Policy class. The list can be thought of as a collection of a specific type.
  1. 13.

    Amend the class, as in Listing 21-15.

     
   // Monthly premium greater than $20
   Policy myPolicyFour = new Policy(455666, "Large_Screen", 29.99, "01/12/2021");
   /*
   Create a strongly typed List to hold the six Policy objects.
   The List of policies will be iterated later
   */
   List<Policy> policies = new List<Policy>()
   { myPolicyOne, myPolicyTwo, myPolicyThree,
     myPolicyFour, myPolicyFive, myPolicySix
   };
 } // End of Main() method
Listing 21-15

Create a list to hold the six Policy instances

We will now create a method that will find the list of policies based on the delegate passed to the method. First, as we are inside the Main() method , we will call the method we will be creating, three times, passing in different delegates in each call.
  1. 14.

    Amend the class, as in Listing 21-16, to call the not-yet-created method.

     
List<Policy> policies = new List<Policy>()
{ myPolicyOne, myPolicyTwo, myPolicyThree,
  myPolicyFour, myPolicyFive, myPolicySix
};
/*
Call the FindPolicesByGivenDelegate method passing
it the delegate to be used
*/
FindPolicesByGivenDelegate("The list of Laptops policies are", policies, HardwareType);
FindPolicesByGivenDelegate("The list of policies due for renewal are", policies, PolicyIsDueForRenewal);
FindPolicesByGivenDelegate("The list of policies with a premium greater than $20.00 are", policies, PremiumGreaterThanTwenty);
} // End of Main() method
Listing 21-16

Call a method, passing it the Policy list and delegate

Now we need to create the method that will

  • Accept a string value to be used as a heading.

  • Accept the list of policies that will be used

  • Accept the delegate that is to be used. This will be in the form of an instantiation FindByDelegate filterMethodToBeUsed so the filterMethodToBeUsed will be one of the methods. It will be the method HardwareType() or the method PolicyIsDueForRenewal() or the method PremiumGreaterThanTwenty().

  • Iterate the list of policies, six policies in our example.

  • Invoke the method referred to by the delegate, which will pass back a true or false, and this calling method then displays the details for the policy if the returned value was true.

  • Use a “divider ” at the end of the method before the next call is made.

  1. 15.

    Amend the class, as in Listing 21-17, to create the method, outside the Main() method but inside the class.

     
    } // End of Main() method
    /*
    In this method we use the delegate, FindByDelegate to
    invoke the method it is pointing to.
    The delegate method name is passed to this method.
    */
    static void FindPolicesByGivenDelegate(string title,
      List<Policy> policies, FindByDelegate filterMethodToBeUsed)
    {
      Console.WriteLine(title);
      foreach (Policy policy in policies)
      {
        if (filterMethodToBeUsed(policy))
        {
          Console.WriteLine($" {policy.PolicyType} policy " +
            $"number {policy.PolicyNumber} is due for renewal " +
            $"on {policy.PolicyEndDate} and the current " +
            $"premium is {policy.MonthlyPremium}");
        } // End of if construct
      } // End of foreach iteration
      // Separate the three filters for display purposes
      Console.WriteLine("----------------------------");
    } // End of FindPolicesByGivenDelegate
  } // End of DelegatePolicy class
} // End of Chapter21 namespace
Listing 21-17

Create the method that accepts the Policy list and delegate

Now we need to create the method to find those policies that are Laptops when the delegate passed in is HardwareType . Remember the delegate is pointing to a method; it refers to a method.
  1. 16.

    Amend the class to create this method called HardwareType, outside the Main() method, as in Listing 21-18.

     
 } // End of FindPolicesByGivenDelegate
 /*********************************************************
 *    Methods that will be pointed to by the delegate     *
 *********************************************************/
 //Check if the policy type is a laptop and return true or false
  static bool HardwareType(Policy policyPassedIn)
  {
    return policyPassedIn.PolicyType.Equals("Laptop");
  } // End of HardwareType method
  } // End of DelegatePolicy class
} // End of Chapter21 namespace
Listing 21-18

Create the HardwareType method

Now we need to create the method to find those policies whose renewal date is less than or equal to a given date. In this case we have hard-coded the data as 30/11/2021, when the delegate passed in is PolicyIsDueForRenewal .
  1. 17.

    Amend the class to create this method called PolicyIsDueForRenewal, outside the Main() method, as in Listing 21-19.

     
    } // End of HardwareType method
    /*
    Check if policy is due for renewal and return true or false
    Here we have, for simplicity hard coded a date
    */
    static bool PolicyIsDueForRenewal(Policy policyPassedIn)
    {
      string today = "30/11/2021";
      return DateTime.Parse(policyPassedIn.PolicyEndDate)<= DateTime.Parse(today);
    } // End of PolicyIsDueForRenewal method
  } // End of DelegatePolicy class
} // End of Chapter21 namespace
Listing 21-19

Create the PolicyIsDueForRenewal method

Now we need to create the method to find those policies whose monthly premium is greater than 20.00.
  1. 18.

    Amend the class, as in Listing 21-20, to create this method called PremiumGreaterThanTwenty, outside the Main() method.

     
    } // End of PolicyIsDueForRenewal method
    /*
    Check if the monthly premium is greater than $20
    and return true or false
    */
    static bool PremiumGreaterThanTwenty(Policy policyPassedIn)
    {
      return policyPassedIn.MonthlyPremium > 20.00;
    } // End of PremiumGreaterThanTwenty method
  } // End of DelegatePolicy class
} // End of Chapter21 namespace
Listing 21-20

Create the PremiumGreaterThanTwenty method

  1. 19.

    Right-click the Chapter21 project in the Solution Explorer panel.

     
  2. 20.

    Choose Properties from the pop-up menu.

     
  3. 21.

    Choose the DelegatePolicy class in the Startup object drop-down list.

     
  4. 22.

    Close the Properties window.

     
  5. 23.

    Click the File menu.

     
  6. 24.

    Choose Save All.

     
  7. 25.

    Click the Debug menu.

     
  8. 26.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 21-3, displaying the details from the invoked methods as referred to by the delegate.

A console window depicts 3 delegates invoked, and lists the laptop policies, and those due for renewal and with a premium of greater than 20 dollars.

Figure 21-3

Multicast delegate invoked complex example

  1. 27.

    Press the Enter key to close the console window.

     

Now that really was a complex application using delegates, so we will probably need to read the code carefully, several times, to fully appreciate what is going on.

Chapter Summary

So, finishing this chapter on delegates, we should see that we have extended our knowledge of methods and that we can use delegates to pass methods to methods. We have seen that when we declare a delegate, it has a similar format to declaring an abstract method, a return type followed by the method signature. We also learned how delegates could be chained, so that multiple methods could be called from a single delegate. This is referred to as multicast delegates.

Wow, what an achievement. This is definitely not basic coding. We are seriously doing some elaborate things with our C# code. We should be immensely proud of the learning to date. In finishing this chapter, we have increased our knowledge further. We are getting very close to our target, which once seemed so far away.

An illustration of concentric circles with two differently colored regions above a text that reads, Our target is getting closer.

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

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