.NET Delegates: Modern-Day Callback Methods

Delegates became more mainstream to the Microsoft programming world when Microsoft included them in the .NET Framework, but they actually have been around in another form for a while. Many programmers will recognize them as callback functions/methods. In fact, a brief discussion regarding the purpose of callback methods before jumping into the specifics of delegates will make delegates easier to understand.

Let’s define callbacks through an example rather than a formal definition. Consider a multi-tier application for loan processing that has a business logic component containing loan-processing rules. During processing, a decision tree determines the applicant’s overall credit rating score. This calculation is complex, often involving a number of variables and taking varying lengths of time to complete. Suppose that while the application is processing an applicant, a loan officer learns something that requires another applicant’s credit score to be updated. Rather than have the UI wait until the processing finishes, which prevents the officer from updating the other applicant’s score, the application makes a request and will notify the UI when processing is complete. In order for this notification to occur, the business logic component needs the address of the method to call in the UI when processing is complete. This is the essence of a callback method. It is a pointer to a method in the client to call when processing is complete. For those familiar with design patterns, this is the observer design pattern.

At this point, alarm bells may be going off in the heads of many readers. I used the dreaded pointer word, which has caused headaches for many programmers and application users alike. Pointers represent just an address in memory and nothing about the method being called, not its parameters nor any return value. The called method pulls all of that information off the call stack. If the address location gets moved, a number of potential problems including malicious code can occur. More likely than malicious code, however, .NET’s managed environment can move items around in memory as it does its internal optimization. Thus, callback methods aren’t type safe and don’t fit into the realm of Microsoft .NET.

Enter delegates. A delegate is essentially a type-safe pointer to a method. The .NET runtime enforces a check against the method signature and only the method with the proper signature will execute.

Using Delegates

Microsoft .NET uses the delegate keyword to define a delegate. The syntax looks similar to any other method declaration where it has a return type and parameters.

Delegate sample code

The following sample code contains a simple example of using delegates. The example is based on the aforementioned example scenario. The console application requests that a loan applicant be processed, and then provides a method to call when the processing is complete. In reality, you’d use a forms application, but a simple console will work for this example:

using System;

namespace CodeGuru.Delegates
{
   /// <summary>
   /// Sample class representing a loan applicant.
   /// </summary>
   public class LoanApplicant
   {
      public double CreditScore = 0;
   }

   /// <summary>
   /// Sample class containing a loan processing example.
   /// </summary>
   public class LoanSystem
   {
      // Loan applicant.
      private LoanApplicant _LoanApplicant = new LoanApplicant();

      // Delegate for processing a loan.
      public delegate void ProcessLoanDelegate(LoanApplicant
                                               loanApplicant);

      // Process the loan application.
      public void ProcessLoanApplication(ProcessLoanDelegate
                                         processLoan)
      {
         // Calculate the credit score
         this.CalculateCreditScore(_LoanApplicant);

         // Execute the callback method
         processLoan(_LoanApplicant);
      }

      // Calculate the applicant's credit score.
      private void CalculateCreditScore(LoanApplicant loanApplicant)
      {
         Random randNumber = new Random();
         loanApplicant.CreditScore = randNumber.Next(100) + .5;
      }
   }

   /// <summary>
   /// Sample application to demonstrate the use of delegates.
   /// </summary>
   class DelegateExample
   {
      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
         DelegateExample example = new DelegateExample();
         LoanSystem loanSystem   = new LoanSystem();
         loanSystem.ProcessLoanApplication(
            new LoanSystem.ProcessLoanDelegate(
               example.DisplayCreditScore));
         Console.ReadLine();
      }


      /// <summary>
      /// Display the loan applicant's credit score.
      /// </summary>
      /// <param name="loanApplicant">Loan applicant</param>
      public void DisplayCreditScore(LoanApplicant loanApplicant)
      {
         Console.WriteLine("Applicant Credit Score: {0}",
         loanApplicant.CreditScore);
      }
   }
}

Output from the example looks similar to Figure 1. The number will vary depending upon what the random number generates.

Figure 1. Output from Delegate Sample Code

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read