Delegates And EventHandler For Events In C#

This is blog, let's see the usage of delegates and why we use EventHandler instead of a delegate.

Firstly, what is delegate?

 
A delegate is like a pointer to a function. It holds the reference of a method. All delegates are derived from System.Delegate class. 
 
Syntax of Delegate =>
 
<access modifier> delegate <return type> <delegate_name>(<parameters>)
 
e.g. => 
  1. public delegate void SendMessage(string message);  
The SendMessage delegate shown above, can be used to point to any method that has same return type & parameters declared with SendMessage. 
 

What is Event?

 
Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers. An event has a publisher, subscriber, notification and a handler. Practically, an event is nothing but an encapsulated delegate.
 
Syntax of Events =>
 
<access modifier> event <delegate_name> EventName;
 
OR
 
<access modifier> event EventHandler<custom event class> EventName;
 
OR
 
<access modifier> event Eventhandler EventName;
 
e.g.
  1. public event MessageLoggedEventHandler MessageLogged;  
The main use of events and delegates is to make objects loosely coupled. I will show you how this helps.
 
Now lets start. We will create a LoggerService which will write the Logs and once the log are written it will send the Mail/Message based on the events subscribed.
 
First let's create a Logger class (Publisher) which will be responsible for publishing the Event. 
  1. /// <summary>  
  2.    ///  Create a Publisher by -  
  3.    ///  1 - Define delegate  
  4.    ///  2 - Define an Event based on delegate  
  5.    ///  3 - Raise the event  
  6.    /// </summary>  
  7.    ///   
  8.    public class Logger  
  9.    {  
  10.        public delegate void DataLoggedEventHandler(object source, EventArgs args);  
  11.        public event DataLoggedEventHandler MessageLogged;  
  12.        public void WriteLog(string message)  
  13.        {  
  14.            Console.WriteLine($"Message: {message}");  
  15.            Thread.Sleep(4000);  
  16.            OnMessageLogged();// raise event  
  17.        }  
  18.        // .Net recommends - Event method should be protected, virtual, void and start with On<Eventname>  
  19.        protected virtual void OnMessageLogged()  
  20.        {  
  21.            if (MessageLogged != null)  
  22.                MessageLogged(this, EventArgs.Empty);  
  23.        }  
  24.    }  
In the above code, we created a delegate (DataLoggedEventHandler) to hold the reference of the method having same signature. Secondly, we created an event for DataLoggedEventHandler delegate which will allow the subscriber to subscribe.
 
Finally, we created an OnMessageLogged() event method responsible for invoking respective method subscribed.
 
Now we will create a MailService class (Subscriber) which would be responsible for sending mail once the event occurs.
  1. public class MailService  
  2.     {  
  3.         public void OnMessageLogged(object source,EventArgs args)  
  4.         {  
  5.             Console.WriteLine("Sending Mail...");  
  6.             Thread.Sleep(3000);  
  7.             Console.WriteLine("Mail Sent");  
  8.         }  
  9.     }  
Note here, the method signature is same as that of delegate. 
 
Now let's create a Main() class which would subscribe the events and will invoke the WriteLog() method. 
  1. public class LoggerService  
  2.     {  
  3.         static void Main()  
  4.         {  
  5.             var logger = new Logger(); // publisher  
  6.             var mailer = new MailService();// subscriber  
  7.  
  8.             #region Subscribe Events  
  9.             logger.MessageLogged += mailer.OnMessageLogged;  
  10.             #endregion  
  11.   
  12.             // Will raise the events  
  13.             logger.WriteLog("This is an error log");  
  14.   
  15.             Console.WriteLine("Event Completed");  
  16.             Console.Read();  
  17.         }  
  18.     }  
Now let's see how it is loosely coupled. Lets say I need to add a new service called MessageService and also want to subscribe the event and invoke action need to be performed OnMessageLogged()  for MessageService.
  1. public class MessageService  
  2.    {  
  3.        public void OnMessageLogged(object source, EventArgs args)  
  4.        {  
  5.            Console.WriteLine("Sending Message...");  
  6.            Thread.Sleep(3000);  
  7.            Console.WriteLine("Message Sent");  
  8.        }  
  9.    }  
Now if I need to plug it similar to MailService then I need to make changes in only Calling(Main) class. 
  1. public class LoggerService  
  2.     {  
  3.         static void Main()  
  4.         {  
  5.             var logger = new Logger(); // publisher  
  6.             var mailer = new MailService();// subscriber  
  7.             var messenger = new MessageService();// subscriber  
  8.  
  9.             #region Subscribe Events  
  10.             logger.MessageLogged += mailer.OnMessageLogged;  
  11.             logger.MessageLogged += messenger.OnMessageLogged;  
  12.             #endregion  
  13.   
  14.             // Will raise the events  
  15.             logger.WriteLog("This is an error log");  
  16.   
  17.             Console.WriteLine("Event Completed");  
  18.             Console.Read();  
  19.         }  
  20.     }  
Run the app and see the output.
 
Okay. Now we are quite clear with the concepts of Events and Delegate and how they both works together. 
 
Now lets say if we need to send the message through event then how can we achieve it.
 
I will create a CustomEvent say LoggerEvent by inheriting from EventArgs. 
  1. public class LoggerEvents : EventArgs  
  2.     {  
  3.         public string Message { getset; }  
  4.     }  
Now we will use this event to send the message. Lets see the required changes in Logger class,
  1. public class Logger  
  2.    {  
  3.        public delegate void DataLoggedEventHandler(object source, EventArgs args);  
  4.        public delegate void DataLoggedEventHandler(object source, LoggerEvents args);  
  5.        public event DataLoggedEventHandler MessageLogged;  
  6.        {  
  7.            Console.WriteLine($"Message: {message}");  
  8.            Thread.Sleep(4000);  
  9.            OnMessageLogged();// raise event  
  10.            OnMessageLogged(message);// raise event  
  11.        }  
  12.        // .Net recommends - Event method should be protected, virtual, void and start with On<Eventname>  
  13.        protected virtual void OnMessageLogged()  
  14.        protected virtual void OnMessageLogged(string message)  
  15.        {  
  16.            if (MessageLogged != null)  
  17.                MessageLogged(this, EventArgs.Empty);  
  18.                MessageLogged(this, new LoggerEvents() { Message = message });  
  19.        }  
  20.    }  
Now with these changes we also need to update the Subscriber classes to accomodate custom event.
  1. public class MailService  
  2.     {  
  3.         public void OnMessageLogged(object source,EventArgs args)  
  4.         public void OnMessageLogged(object source, LoggerEvents args)  
  5.         {  
  6.             Console.WriteLine("Sending Mail...");  
  7.             Thread.Sleep(3000);  
  8.             Console.WriteLine("Mail Sent");  
  9.             Console.WriteLine($"Mail Sent with message - {args.Message}");  
  10.         }  
  11.     }  
  1. public class MessageService  
  2.    {  
  3.        public void OnMessageLogged(object source, EventArgs args)  
  4.        public void OnMessageLogged(object source, LoggerEvents args)  
  5.        {  
  6.            Console.WriteLine("Sending Message...");  
  7.            Thread.Sleep(3000);  
  8.            Console.WriteLine("Message Sent");  
  9.            Console.WriteLine($"Message Sent with message - {args.Message}");  
  10.        }  
  11.    }  
Now just run it and the output would be same.
 
Finally, one more enhancement to Logger class (Publisher) to use EventHandler instead of delegate. .Net came up with EventHandler from .Net Framework 2.0 version so we can use it instead of delegates. Lets see how we can make the changes and get the same output with minimize code. 
  1. public class Logger  
  2.     {  
  3.         //public delegate void DataLoggedEventHandler(object source, LoggerEvents args);       
  4.   
  5.         //public event DataLoggedEventHandler MessageLogged;  
  6.   
  7.         public event EventHandler<LoggerEvents> MessageLogged;// if we want to pass any message and used custom EventHandler  
  8.         //public event EventHandler MessageLogged;// if we do not want to pass any message hence not using custom EventHandler  
  9.   
  10.         public void WriteLog(string message)  
  11.         {  
  12.             Console.WriteLine($"Message: {message}");  
  13.             Thread.Sleep(4000);  
  14.             OnMessageLogged(message);// raise event  
  15.         }  
  16.   
  17.         // .Net recommends - Event method should be protected, virtual, void and start with On<Eventname>  
  18.         protected virtual void OnMessageLogged(string message)  
  19.         {  
  20.             if (MessageLogged != null)  
  21.                 MessageLogged(thisnew LoggerEvents() { Message = message });  
  22.         }  
  23.     } 
With the above changes, we removed creation of delegate and instead used the EventHandler. Run the app you should get the same o/p.
 
Thats it. Here, I covered the concepts of events, delegates and event handlers in  C#.