What is Event
Events are signal for action/notification and a mechanism to communicate between objects. The object raises the event but it doesn’t explicitly require to know the object that will handle the event. Events can help us to create a loosely coupled application. We can also use the events for extending the application.
The object that raises the event do not need to explicitly know the object that will handle the event. EventArgs is passed by event which is also known as event data.
Event handler is responsible for receiving and processing data from a delegate. Eventargs is responsible for encapsulating event data.
What is Delegate
A delegate in C# is similar to a function pointer of C or C++. It is a type that references methods. We can also say that a delegate is the pipeline between an event and event handler.
Let’s try to understand it with an example,
Sending email to all users and writing message on Console with the use of delegate
- namespace DelegateExample
- {
- publicclassProgram
- {
- publicdelegatevoidMyCustomDelegate();
- publicstaticvoid Main(string[] args)
- {
- MyCustomDelegate myCustomDelegate = newMyCustomDelegate(SendMailtoAllGoldMembers);
- myCustomDelegate.Invoke();
- }
-
- publicstaticvoid SendMailtoAllGoldMembers()
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
- Console.WriteLine($ "Email Id {emailId} : mail sent");
- }
- }
-
- publicstaticList < string > GetListofEmailIdForGoldMembers()
- {
- List < string > ListOfEmailIds = newList < string > ();
- for (int i = 1; i <= 100; i++)
- {
- ListOfEmailIds.Add($ "member{i.ToString("
- D4 ")}@gmail.com");
- }
-
- return ListOfEmailIds;
- }
-
- }
- }
But you may ask the question that why we need to use the delegate, since we can call the method SendMailtoAllGoldMembers() directly. Yes we can also write it as follows:
Sending email to all users and writing message on Console window without using any delegate - publicclassProgram
- {
- publicstaticvoid Main(string[] args)
- {
- SendMailtoAllGoldMembers();
- }
-
- publicstaticvoid SendMailtoAllGoldMembers()
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
- Console.WriteLine($ "Email Id {emailId} : mail sent");
- }
- }
-
- publicstaticList < string > GetListofEmailIdForGoldMembers()
- {
- List < string > ListOfEmailIds = newList < string > ();
- for (int i = 1; i <= 100; i++)
- {
- ListOfEmailIds.Add($ "member{i.ToString("
- D4 ")}@gmail.com");
- }
-
- return ListOfEmailIds;
- }
-
- }
But in the above program you can see that it is tightly coupled &SendMailtoAllGoldMembers() is doing the task of writing on console window.
Writing message in log file rather than Console Window
Now suppose my requirement has been changed and I want to write all these messages in log file rather than displaying it on console window.
So you can write the program like below:
- publicstaticvoid Main(string[] args)
- {
- SendMailtoAllGoldMembers();
- }
-
-
- publicstaticvoid SendMailtoAllGoldMembers()
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
-
- System.IO.File.AppendAllText(@ "D:\\delexample\log.txt", $ "\n Email Id {emailId} : mail sent");
- }
- }
Writing message in database table rather than log file
Suppose again requirement changes and I need to insert the email sent status into a database table again I need to change it.
- publicstaticvoid SendMailtoAllGoldMembers()
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
-
-
- InsertTheLogDetailsIntoDatabase($ "Email Id {emailId} : mail sent");
- }
- }
Handling all the above scenarios with delegate
Let us see how I can handle it using delegate,
- Create a delegate that accept string as argument
- publicdelegatevoidEmailSentNotificationDelegate(string notificationMsg);
- Create a method to send the email messages
- publicstaticvoid SendMailtoAllGoldMembers(EmailSentNotificationDelegate EmailSentNotification)
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
- EmailSentNotification($ "Email Id {emailId} : mail sent");
-
- }
- }
- Create a method for writing log in console window
- publicstaticvoid WriteEmailSentNotificationOnConsole(string notificationMsg)
- {
- Console.WriteLine(notificationMsg);
- }
- Create a method to write log in log file
- publicstaticvoid WriteEmailSentNotificationinLogFile(string notificationMsg)
- {
- System.IO.File.AppendAllText(@ "D:\\delexample\log.txt", $ "\n {notificationMsg}");
- }
- Create a method to write log in database
- publicstaticvoid WriteEmailSentNotificationinDatabase(string notificationMsg)
- {
- InsertTheLogDetailsIntoDatabase(notificationMsg);
- }
- Calling the SendMailtoAllGoldMembers method
a. Calling SendMailtoAllGoldMembersmethod and writing log in console window,
- SendMailtoAllGoldMembers(WriteEmailSentNotificationOnConsole);
b. Calling SendMailtoAllGoldMembers method and writing log in log file,
- SendMailtoAllGoldMembers(WriteEmailSentNotificationinLogFile);
c. Calling SendMailtoAllGoldMembers method and writing log in database,
- SendMailtoAllGoldMembers(WriteEmailSentNotificationinDatabase);
Complete code
- publicclassProgram
- {
- publicdelegatevoidEmailSentNotificationDelegate(string notificationMsg);
- publicstaticvoid Main(string[] args)
- {
- SendMailtoAllGoldMembers(WriteEmailSentNotificationOnConsole);
-
-
- }
-
- publicstaticvoid SendMailtoAllGoldMembers(EmailSentNotificationDelegate EmailSentNotification)
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
- EmailSentNotification($ "Email Id {emailId} : mail sent");
-
- }
- }
-
- privatestaticvoid InsertTheLogDetailsIntoDatabase(string msg)
- {
-
- }
-
- publicstaticList < string > GetListofEmailIdForGoldMembers()
- {
- List < string > ListOfEmailIds = newList < string > ();
- for (int i = 1; i <= 100; i++) {
- ListOfEmailIds.Add($ "member{i.ToString("
- D4 ")}@gmail.com");
- }
-
- return ListOfEmailIds;
- }
-
- publicstaticvoid WriteEmailSentNotificationOnConsole(string notificationMsg)
-
- Console.WriteLine(notificationMsg);
- }
-
-
- publicstaticvoid WriteEmailSentNotificationinLogFile(string notificationMsg)
- {
- System.IO.File.AppendAllText(@ "D:\\delexample\log.txt", $ "\n {notificationMsg}");
- }
- publicstaticvoid WriteEmailSentNotificationinDatabase(string notificationMsg)
- {
- InsertTheLogDetailsIntoDatabase(notificationMsg);
- }
-
- }
We can represent the whole logic using the following screenshot.
So this is the way to create custom delegate in C# but you may be thinking that where .NET Framework use the delegate or what are the in-built delegate used for event handling.
Delegates &Event Handling with Windows Form Application
I am creating a simple windows application.
In Form1.Designer.cs the code snippet is,
-
-
-
- this.button1.Location = new System.Drawing.Point(37, 26);
- this.button1.Name = "button1";
- this.button1.Size = new System.Drawing.Size(75, 23);
- this.button1.TabIndex = 0;
- this.button1.Text = "button1";
- this.button1.UseVisualStyleBackColor = true;
- this.button1.Click += new System.EventHandler(this.button1_Click);
And code snippet for Form1.cs is, - privatevoid button1_Click(object sender, EventArgs e)
- {
-
- }
So you can see System.EventHandler is a delegate which is associating button1_Click method with button1 click event.
If you press F12 (go to definition) on EventHandler then you will find the following code snippet.
- namespace System
- {
-
-
-
-
-
-
-
-
-
-
- [ComVisible(true)]
- publicdelegatevoidEventHandler(object sender, EventArgs e);
- }
Delegates & Event Handling with WPF Application
Now I am creating a WPF application
MainWindow.xaml
- <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="78,49,0,0" VerticalAlignment="Top" Width="154" Height="54" Click="button_Click"/>
MainWindow.g.i.cs - this.button.Click += new System.Windows.RoutedEventHandler(this.button_Click);
MainWindow.xaml.cs - privatevoid button_Click(object sender, RoutedEventArgs e)
- {
-
- }
In the above example you can see RoutedEventHandler is a delegate, Now press F12 (go to definition) for RoutedEventHandler. You will find the following definition,
- namespace System.Windows
- {
-
-
-
-
-
-
-
-
-
-
-
- publicdelegatevoidRoutedEventHandler(object sender, RoutedEventArgs e);
- }
Delegates &Event Handling with ASP.NET Web Form
Now create an ASP.NET web Form application.
On .aspx page the following code snippet is written
- <formid="form1" runat="server">
- <div>
-
- <asp:ButtonID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
-
- </div>
- </form>
And on .cs file the following code snippet is written - protectedvoid Button1_Click(object sender, EventArgs e)
- {
-
- }
Here Button1_Click written with OnClick="Button1_Click" is delegate but you may be thinking that where the delegate is calling the method. Actually it is happening behind the scene. This is a syntactic sugar of ASP.NET web form.
Till now we have created only simple delegate let’s deep dive into it and use the custom delegate in more detail.
As we know delegate keyword is used to define custom delegate, there are 2 types of delegates,
- Single cast delegate
- Multicast delegate
Multicast delegate
A multicast delegate can reference more than one delegate function. It tracks delegate references using an invocation list and all the delegates in the list invoked sequentially.
We can take above example for multiple invocation list.
Code
- publicstaticvoid Main(string[] args)
- {
- EmailSentNotificationHandler del1 = newEmailSentNotificationHandler(WriteEmailSentNotificationOnConsole);
- del1 += WriteEmailSentNotificationinLogFile;
- del1 += WriteEmailSentNotificationinDatabase;
- SendMailtoAllGoldMembers(del1);
- }
The rest part of the code will be the same.
Now I can write log in a log file and display log message on console window and also data is being inserted in database. So I am doing all the 3 logging work.
As delegate provide the functionality to add & remove delegate any time so we can unsubscribe it any time
Example: - publicstaticvoid Main(string[] args)
- {
-
- EmailSentNotificationHandler del1 = newEmailSentNotificationHandler(WriteEmailSentNotificationOnConsole);
- del1 += WriteEmailSentNotificationinLogFile;
- del1 += WriteEmailSentNotificationinDatabase;
- SendMailtoAllGoldMembers(del1);
- }
- publicstaticvoid SendMailtoAllGoldMembers(EmailSentNotificationHandler EmailSentNotification)
- {
- foreach(var emailId in GetListofEmailIdForGoldMembers())
- {
-
- System.Threading.Thread.Sleep(2000);
- EmailSentNotification -= WriteEmailSentNotificationinDatabase;
- EmailSentNotification -= WriteEmailSentNotificationinLogFile;
- EmailSentNotification($ "Email Id {emailId} : mail sent");
- }
- }
So in the above code I have removed WriteEmailSentNotificationinDatabase & WriteEmailSentNotificationinLogFile inside SendMailtoAllGoldMembers() method so now it will write message only on console window.
Delegate cannot be called like a method
We cannot call a delegate directly like a method; if we try to call it will throw the exception e.g. if I write
EmailSentNotificationHandler($"Email Id {emailId} : mail sent");
It will give the compile time error
Non-invocable member 'Program.EmailSentNotificationHandler' cannot be used like a method.
Calling a delegate using Invoke method
- publicstaticvoid Main(string[] args)
- {
- EmailSentNotificationHandler del1 = newEmailSentNotificationHandler(WriteEmailSentNotificationOnConsole);
- del1 += WriteEmailSentNotificationinLogFile;
- del1 += WriteEmailSentNotificationinDatabase;
- del1.Invoke($ "Email Id {emailId} : mail sent");
- }
Calling a delegate by passing it as parameter - publicstaticvoid Main(string[] args)
- {
- EmailSentNotificationHandler del1 = newEmailSentNotificationHandler(WriteEmailSentNotificationOnConsole);
- del1 += WriteEmailSentNotificationinLogFile;
- del1 += WriteEmailSentNotificationinDatabase;
- SendMailtoAllGoldMembers(del1);
-
- }
Calling a delegate by passing parameters inside the delegate object.
We can also call the delegate by passing parameters inside the delegate object
Example:
- publicdelegatevoidLogger(string message);
- Logger log = newLogger(WriteMsgOnConsole);
- log("Transaction completed successfully");
Complete code - classProgram
- {
- publicdelegatevoidLogger(string message);
- staticvoid Main(string[] args)
- {
- Logger log = newLogger(WriteMsgOnConsole);
- log("Transaction completed successfully");
- }
-
- publicstaticvoidWriteMsgOnConsole(string message)
- {
- Console.WriteLine(message);
- }
- }
You may have noticed that I am using a lot of static method while explaining the delegate but it’s not mandatory that a method should be static. I am using static method just for my convenience.
We can also write the above example without using the static method.
- classLogging
- {
- publicdelegatevoidLogger(string message);
- staticvoid Main(string[] args)
- {
- Logging logging = newLogging();
- Logger log = newLogger(logging.WriteMsgOnConsole);
- log("Transaction completed successfully");
- }
-
- publicvoid WriteMsgOnConsole(string message)
- {
- Console.WriteLine(message);
- }
- }
For using delegate name of the method and name of the delegate doesn’t matter only the thing that matter is return type & parameter type of the method should be same as that of delegate.
I would like to clarify that all the delegates in C# is by default multicast delegate. I am listening from many years that multicast delegate must have return type void but I do not agree with this statement. I have used multicast delegate that is not having the return type as void but it is not throwing any exception.
Using int(System.Int32) Type Multicast delegate
Multicast delegate can reference more than one delegate function and it tracks the delegate references using an invocation list.
You may be thinking that if you are referencing more than 1 delegate function then how it will be executed and what will be the sequence for it. It will be executed one by one and invoked sequentially in the same order in which it has been added.
Let’s try to understand it with an example:
Create a delegate which accept 2 int parameters and return an int value
public delegate int SomeActionPerformedHandler(int x, int y);
Create 4 different methods each of them will accept 2 int parameters and return an int value - public static int FirstWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("FirstWorkPerformed Executed");
- return 10;
- }
-
- public static int SecondWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("SecondWorkPerformed Executed");
- return 20;
- }
-
- public static int ThirdWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("ThirdWorkPerformed Executed");
- return 30;
- }
-
- public static int FourthWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("FourthWorkPerformed Executed");
- return 40;
- }
Adding all the methods in invocation list and calling it. - SomeActionPerformedHandler mycustomdelegate1 = newSomeActionPerformedHandler(FirstWorkPerformed);
- SomeActionPerformedHandler mycustomdelegate2 = newSomeActionPerformedHandler(SecondWorkPerformed);
- SomeActionPerformedHandler mycustomdelegate3 = newSomeActionPerformedHandler(ThirdWorkPerformed);
- SomeActionPerformedHandler mycustomdelegate4 = newSomeActionPerformedHandler(FourthWorkPerformed);
-
- mycustomdelegate1 += mycustomdelegate2;
- mycustomdelegate1 += mycustomdelegate3;
- mycustomdelegate1 += mycustomdelegate4;
-
-
-
-
- int delegateRuturbedValue = mycustomdelegate1(10, 20);
Complete code - classProgram
- {
- publicdelegateintSomeActionPerformedHandler(int x, int y);
- staticvoid Main(string[] args)
- {
-
- SomeActionPerformedHandler mycustomdelegate1 = newSomeActionPerformedHandler(FirstWorkPerformed);
- SomeActionPerformedHandler mycustomdelegate2 = newSomeActionPerformedHandler(SecondWorkPerformed);
- SomeActionPerformedHandler mycustomdelegate3 = newSomeActionPerformedHandler(ThirdWorkPerformed);
- SomeActionPerformedHandler mycustomdelegate4 = newSomeActionPerformedHandler(FourthWorkPerformed);
-
- mycustomdelegate1 += mycustomdelegate2;
- mycustomdelegate1 += mycustomdelegate3;
- mycustomdelegate1 += mycustomdelegate4;
-
-
-
-
- intdelegateRuturbedValue = mycustomdelegate1(10, 20);
- Console.WriteLine($ "returned value from the delegate is: {delegateRuturbedValue}");
- Console.ReadKey();
-
- }
- publicstaticintFirstWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("FirstWorkPerformed Executed");
- return 10;
- }
-
- publicstaticintSecondWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("SecondWorkPerformed Executed");
- return 20;
- }
-
- publicstaticintThirdWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("ThirdWorkPerformed Executed");
- return 30;
- }
-
- publicstaticintFourthWorkPerformed(int x, int y)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("FourthWorkPerformed Executed");
- return 40;
- }
- }
Output
So you can see the output screen of console it is a proof for the following statements:
- All the methods executed in the same order in which delegate references has been added.
- In case of multicast delegate only the value returned from the last delegate reference.
- It’s not mandatory to use void type for multicast delegate we can use other return type also with multicast delegate. And it is not throwing any exception.
So till now you have learnt that for multicast delegate we must delegate with same return type and same number of parameter.
But suppose you are an interviewee and interviewer ask that can I use default parameter with delegates? Let’s try it and see the result.
I have changed the 2nd parameter as default parameter,
- publicstaticintFirstWorkPerformed(int x, int y = 0)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("FirstWorkPerformed Executed");
- return 10;
- }
And it works.
Now change the 2nd parameter of delegate and pass some default value,
- publicdelegateintSomeActionPerformedHandler(int x, int y=0);
It also works, Now add 3rd parameter for method FirstWorkPerformed.
e.g. - publicstaticintFirstWorkPerformed(int x, int y, int z = 0)
- {
-
- System.Threading.Thread.Sleep(500);
- Console.WriteLine("FirstWorkPerformed Executed");
- return 10;
- }
Then it will give error while adding the method reference in delegate,
I was just giving a twist that while facing an interview an interviewer may ask such type of question.
Removing from Invocation list, -
- mycustomdelegate1 -= mycustomdelegate2;
- mycustomdelegate1 -= mycustomdelegate3;
- mycustomdelegate1 -= mycustomdelegate4;
But as we know that C# is more than 15 year old language and since that a lot of improvement has been done in C#.
Whatever the concept I have explained in this article is being used from C# 1.0 but later on a lot of changes has been happened in C#.
Before C# 2.0 we used to call delegate with named method but later on in C# 2.0 Microsoft introduced a new feature for calling delegate known as anonymous method and later on in C# 3.0 it also introduced lambda expression.
Later on some other in-built delegate has been introduced,
Delegate, Action,Action<T>, Action<T1, T2>>, Action<T1, T2, T3>, Action<T1, T2, T3, T4>, AppDomainInitializer, AssemblyLoadEventHandler, AsyncCallback, Comparison<T>, ConsoleCancelEventHandler, CrossAppDomainDelegate, EventHandler, EventHandler<TEventArgs>,
Func<TResult> , Func<T, TResult>
,Func<T1, T2, TResult> , Func<T1, T2, T3, TResult> ,Func<T1, T2, T3, T4, TResult> ,Predicate<T>, ResolveEventHandler, UnhandledExceptionEventHandler etc.
But I am not going to explain those things in this article because it will be mix up and confuse. I will explain Anonymous method, Lambda expression and in-built delegates in next article.