Delegates And Events - What Are They And Their Use

I would like to explain one of the important usage of Delegates and Events.

Delegates

In most of the blogs, you can find the answer that Delegates are similar to function pointers in C/C++, and we can use Delegate to call methods at runtime.

I will explain one of the real need of Delegates: First, we need to understand what the Publisher subscriber model is. 

"Publish/subscribe messaging, or pub/sub messaging, is a form of asynchronous service-to-service communication used in serverless and microservices architectures. In a pub/sub model, any message published to a topic is immediately received by all of the subscribers to the topic. Pub/sub messaging can be used to enable event-driven architectures, or to decouple applications in order to increase performance, reliability, and scalability".
 
Let's take one example and understand more about Publisher/Subscriber model and Delegates.

I have an ApplicationProcessing Service which is responsible to process the Application and my current requirement is when the Application processing is finished we should send an email to the admin team/IT ops team.

Let's look at my Services classes.

ApplicationProcessingService.cs
  1. public class ApplicationProcessingService : IAppProcessingService  
  2.    {  
  3.        private readonly IEmailService _emailService;          
  4.   
  5.        public ApplicationProcessingService(IEmailService emailService)  
  6.        {  
  7.            _emailService = emailService;              
  8.        }  
  9.   
  10.        public void ProcessApplications(IApplication application)  
  11.        {  
  12.            Thread.Sleep(2000);     
  13.            //My application processing code goes here --- Adding some Thread.Sleep                      
  14.            _emailService.SendEmail(application);                                 
  15.        }  
  16.    }  
My EmailSerice.cs Service 
  1. public partial class EmailService : IEmailService  
  2.     {          
  3.         public void SendEmail(IApplication application)  
  4.         {  
  5.             Console.WriteLine("Sending Email to " + application.ApplicantEmail);  
  6.         }  
  7.     }   
So far everything is fantastic. Now, we have another change request saying, we need to add extra notification system to our application processing (it can be Text Message/ some other notification system).

What will you do?

Are you going to Inject ITextMessageService and update ApplicationProcessing Service to call TextMessageService provider? 

This is not a good practice, we have to focus now on "O" in solid priniciples.
 
"Software entities … should be open for extension, but closed for modification."

Here we need to think about Delegates
 
As per the change request, we definitely need another notification provider class, lets build TextMessageService
  1. public class TextMessageService : ITextMessageService  
  2.    {  
  3.        public void SendMessage(IApplication application)  
  4.        {  
  5.            Console.WriteLine("Sending Text Message to " + application.ApplicantPhone);  
  6.        }  
  7.    }   
 Modify our AppProcessingService (I have created another class with different name for this demo) and add a delegate TransmitMessageToconsumers(IApplication application)
  1. public class ApplicationProcessingServiceWithDelegates : IAppProcessingService  
  2.    {  
  3.        public delegate void TransmitMessageToConsumers(IApplication application);  
  4.        public TransmitMessageToConsumers TransmitMessageToConsumerDelegate = null;  
  5.   
  6.        public void ProcessApplications(IApplication application)  
  7.        {  
  8.            Thread.Sleep(1000);  
  9.            Console.WriteLine("Application processing is done for the Applicant " + application.ApplicantName);  
  10.            TransmitMessageToConsumerDelegate?.Invoke(application);                    
  11.        }  
  12.    }  
In our client application I need to make a few updates to use this Delegate.
 
My client application code: 
  1. public class DelegatesClient  
  2.    {  
  3.        public static void PerformApplicationProcessing()  
  4.        {  
  5.            IApplication application = new Application();  
  6.            application.ApplicantEmail = "[email protected]";  
  7.            application.ApplicantName = "xxxxx";  
  8.            application.ApplicantPhone = "+xxxxxx";  
  9.            application.ApplicationDOB = new DateTimeOffset(1991, 09, 25, 0, 0, 0, TimeSpan.Zero);  
  10.            application.ApplicationId = 123456;  
  11.   
  12.            var appProcessingServiceInstance = new ApplicationProcessingServiceWithDelegates();  
  13.            var emailSeriveInstance = new EmailService();  
  14.            var textMessageServiceInstance = new TextMessageService();  
  15.   
  16.            appProcessingServiceInstance.TransmitMessageToConsumerDelegate += emailSeriveInstance.SendEmail;  
  17.            appProcessingServiceInstance.TransmitMessageToConsumerDelegate += textMessageServiceInstance.SendMessage;  
  18.            appProcessingServiceInstance.ProcessApplications(application);  
  19.   
  20.            //Problem with Delegates --> Data Encapsulation  
  21.            ApplicationProcessingServiceWithDelegates.TransmitMessageToConsumers text = new ApplicationProcessingServiceWithDelegates.TransmitMessageToConsumers(MyClassMethod);  
  22.            text(application);  
  23.        }  
Observe that we are registering two methods to Delegate and in my ProcessApplications method I am invoking the delegate. If I run my application ProcessApplication method Delegate will call Sendmail and SendMessage methods. In future, if we want to skip sending mail we can detach Sendmail process without touching AppProcessing Service.
 
Everything is cool with Delegate. Now, there is a problem with the above code, that is Delegate is exposed to client and client can do whatever they want (make Delegate instance null or some other operations). That is not good. we need to hide exposing this delegate to the public.
 
Events will solve this Data Encapsulation problem of Delegates.
 
Let's look at my modified code with Events.
 
AppProcessingService.cs
  1. public class ApplicationProcessingWithEvents : IAppProcessingService  
  2.     {  
  3.         public delegate void TransmitMessageToConsumers(IApplication application);  
  4.         public event TransmitMessageToConsumers TrasmitMessageToConsumerEvent;  
  5.         public void ProcessApplications(IApplication application)  
  6.         {  
  7.             Thread.Sleep(1000);  
  8.             Console.WriteLine("Application processing is done for the Applicant " + application.ApplicantName);  
  9.             OnTransmitMessageToConsumers(application);             
  10.         }  
  11.   
  12.         protected virtual void OnTransmitMessageToConsumers(IApplication application)  
  13.         {  
  14.             TrasmitMessageToConsumerEvent?.Invoke(application);  
  15.         }  
  16.     }   
I have created an Event and that event is responsible for Inovoking the Methods registered by client.
 
Let's look at my client code.
  1. public class EventsClient  
  2.    {  
  3.        public static void PerformApplicationProcessing()  
  4.        {  
  5.            IApplication application = new Application();  
  6.            application.ApplicantEmail = "[email protected]";  
  7.            application.ApplicantName = "xxxxxx";  
  8.            application.ApplicantPhone = "+91XXXXXX";  
  9.            application.ApplicationDOB = new DateTimeOffset(1991, 09, 25, 0, 0, 0, TimeSpan.Zero);  
  10.            application.ApplicationId = 123456;  
  11.   
  12.            var appProcessingServiceInstance = new ApplicationProcessingWithEvents();  
  13.            var emailSeriveInstance = new EmailService();  
  14.            var textMessageServiceInstance = new TextMessageService();  
  15.   
  16.            appProcessingServiceInstance.TrasmitMessageToConsumerEvent += emailSeriveInstance.SendEmail;  
  17.            appProcessingServiceInstance.TrasmitMessageToConsumerEvent += textMessageServiceInstance.SendMessage;              
  18.            appProcessingServiceInstance.ProcessApplications(application);             
  19.        }          
  20.    }  
I have registerd those events from the client code and whenever the app processing is done, OnTransmitMessageToCosumenrEvent will trigger the registered methods.
 
App processing service event will not allow client code to access Delegate, you will get a compile time error when you try to access it. In this way, we can encapsulate the Delegates. 
 
I have attached the sample code block to this blog.
 
Have a good day.