In general, we call delegates function pointers, meaning delegate object stores a reference of a method.
The delegate is a type that defines a signature, that is, the return value type and parameter list types for a method.
Delegate type can be used to declare a variable and associate its instance with any method with the same signature as the delegate then finally invoke (or call) the method through the delegate instance. Delegate allows us to implement different implementation in different methods (a kind of polymorphism - many forms)
Let’s have a look at the example to understand how delegate works.
Create a delegate that accepts two integer type variables and returns integer value.
Define methods that have the same signature as calculate but they carry out different operations like multiplication, subtraction and addition mentioned below,
Declare & create the delegate object multiplyCalculate that references the method Multiply.
Declare & create the delegate object subtractCalculate that references the method Subtract.
Declare & create the delegate object addCalculate that references the method Add,
- Calculate addCalculate = new Calculate(Add);
Invoking delegate
- Console.WriteLine("Invoking delegate ");
- multiplyCalculate(4, 3);
- subtractCalculate(4, 3);
- addCalculate(4, 3);
Output
Multicast Delegate
In the above code we saw that for the delegate objects invoked one by one (in a sequential manner), at the same time .NET common language runtime allows us to combine and assign multiple objects to one delegate instance by using the += operator. The multicast delegate contains a list of the assigned delegates. When the multicast delegate is called, it invokes the delegates in the list in order they added. Only delegates of the same type can be combined.
Declare & create the delegate object of type Calculate (delegate) and later combine delegate object and assign it to
- Calculate calculateDelegate = new Calculate(Multiply);
- calculateDelegate += new Calculate(Subtract);
- calculateDelegate += new Calculate(Add);
Invoking delegate
- Console.WriteLine("Invoking delegate multi-cast Delegate");
- calculateDelegate(4, 3);
Output
In the same way we can remove methods from the delegate instance by using -= operator
- calculateDelegate -= new Calculate(Subtract);
- Console.WriteLine("Invoking delegate multi-cast Delegate");
- calculateDelegate(5, 4);
Output
SO far we were using a Named method to instantiate a delegate, the named method is passed as a parameter.
Anonymous methods As Delegate
We can use another concept called anonymous methods introduced in C# 2.0 to write unnamed inline statement blocks that can be executed in a delegate invocation
- Calculate multiplyAnonymousCalculate = delegate(int x, int y)
- {
- Console.WriteLine("Hello Multiply !!");
- Console.WriteLine(" Multiply {0} and {1} gives result {2}", y, x, x * y);
- return x * y;
- };
-
- multiplyAnonymousCalculate(4, 3);
Lambda Expression As Delegate
We can use a third concept called Lambda Expression introduced in C# 3.0 to write unnamed inline statement blocks that can be executed in a delegate invocation
- Calculate multiplyAnonymousCalculate = (int x, int y) =>
- {
- Console.WriteLine("Hello Multiply !!");
- Console.WriteLine(" Multiply {0} and {1} gives result {2}", y, x, x * y);
- return x * y;
- };
-
- multiplyAnonymousCalculate(4, 3);
Delegate Accessibility
By default Delegates declared directly with a namespace are internal but can be declared as public whereas Delegates declared directly within a class are private but can be declared as public
When to use Delegates
Delegates can be used to define callback methods – using BeginInvoke method we can make an asynchronous call to perform time consuming tasks. BeginInvoke method returns a reference to an object implementing the IAsyncResult interface immediately and does not wait for the asynchronous call to complete. BeginInvoke method accepts two additional optional parameters called the callback parameter and the state parameter. Callback parameter is a reference of delegate object that will be executed when an asynchronous call or process is completed.
Now let's look at an example of defining and using a delegate to make an asynchronous call. In this example we have class or method to upload picture. Assuming that the uploading might take more time than expected, it is decided to make the method asynchronous and allow the client to provide a callback method
First define a method to upload the image
- public static void UploadPicture(string imagePath)
- {
- for (int iCount = 0; iCount < 10; iCount++)
- {
- Console.WriteLine("Uplodaing continues.....");
- Thread.Sleep(100);
- }
- }
Define a delegate similar to the signature of method UploadPicture,
- delegate void UploadPictureCallBack(string imagePath);
Define a method to be called when asynchronous operation completed
- public static void UploadPictureCompleted(IAsyncResult result)
- {
- if ((result != null) && (result.IsCompleted))
- Console.WriteLine("Completed");
- }
Finally make an asynchronous call and pass callback method
- public static void ExecuteDelegate()
- {
- UploadPictureCallBack calculateDelegate = new UploadPictureCallBack(UploadPicture);
- calculateDelegate.BeginInvoke("", new AsyncCallback(UploadPictureCompleted), null);
- }
Multicasting - Sequential processing
Some time we would like to call some methods in a sequential manner which can be done by using multicast delegate. This is already explained in the multicast example shown above.
Events - Publisher subscriber model
We can use events to create a pure publisher / subscriber model. In the next section, I have explained Events and how delegate and events are associated or work together.
An event is a notification raised by one object to notify other objects that some action has happened. In C#, we define event using keyword “event” whereas in VB.Net we define event using keyword “Event”
- public event EventHandler DatabaseConnectionOpened;
If we look at the declaration, we easily make out public is the access modifier, event is the keyword used to define an event, “DatabaseConnectionOpened” is the event name but what is EventHandler?
The answer is: EventHandler is a type that represents reference to method with a particular parameter list and return type. Doesn't it look like we are talking about some specific object? Yes, it is a Delegate.
So if we look at the event declaration closely, we can say that event named “DatabaseConnectionOpened” is a type of EventHandler but that’s not true actually. EventHandler is a delegate and it is associated with the event named “DatabaseConnectionOpened” to hold a reference of a method to be called when the event is raised .
Relationship between Delegate and Event
Events are based on delegate model meaning in event based communication, one object raises the event and the other object captures and handles that event and responds to it so handler method is nothing but a method that is invoked through delegate.
Delegate helps us in connecting an event with its handler method; delegate helps us in identifying the method that provides the response to the event
.Net Common Language Runtime provides a predefined delegate named “EventHandler” delegate that specifically represents an event handler method for an event that does not generate data
Let’s have a look at the example to understand the relationship between Event and Delegates i.e., how event is declared, delegate associated with the event, and how to raise an event and consume that event in a better way:
Use Case
Stock Exchange consolidates trading data at the end of the day, then uploads a file on the server location and finally raises an event when upload is completed to notify the users.
Option 1
When there is no need to pass data with the event, preferably use publicdelegatevoid EventHandler(object sender, EventArgs e)
- public class StockExchange
- {
-
-
- public event EventHandler FileUploaded;
-
- public void ProcessData()
- {
- for (int iCount = 0; iCount <= 10; iCount++)
- {
- Console.WriteLine("Data Processing is in progress .......");
- }
-
- UploadFile();
- }
-
- private void UploadFile()
- {
- Console.WriteLine("File Upload Completed");
- OnFileUploaded();
- }
-
-
-
-
- private void OnFileUploaded()
- {
-
- FileUploaded?.Invoke(this, EventArgs.Empty);
- Console.WriteLine("Event FileUploaded raised");
- }
- }
If we look at the implementation of the above class StockExchange, we have declared an event named FileUploaded that is raised when file upload is completed. As we can see that the .NET framework in-built delegate publicdelegatevoidEventHandler(object sender, EventArgs e) is associated with the event, a method with similar signature as delegate can be declared and assigned to the event.
- private static void StockExchange_FileUploaded(object sender, EventArgs e)
- {
- Console.WriteLine("StockExchange Class Event FileUploaded handled.");
- }
-
-
- static void Main(string[] args)
- {
- StockExchange stockExchange = new StockExchange();
-
-
- stockExchange.FileUploaded += StockExchange_FileUploaded;
- stockExchange.ProcessData();
- Console.ReadLine();
- }
Output
Option 2
When there is a need to pass data with the event, preferably use publicdelegatevoid EventHandler<TEventArgs>(object sender, TEventArgs e) delegate instead of EventHandler TEventAgrs is the generic type and an instance of class derived from EventArgs can be assigned to it.
Let’s have a look at the example to understand how to use publicdelegatevoid EventHandler<TEventArgs>(object sender, TEventArgs e), raise an event and consume it.
Create a class to store and pass event data inheriting EventArgs,
- public class FileUploadedEventArgs : EventArgs
- {
- public string FileLocation { get; set; }
- }
Change the current delegate and associate EventHandler<FileUploadedEventArgs> delegate with the event, because event data is to be provided when event is raised.
- public class StockExchange
- {
-
-
- public event EventHandler<FileUploadedEventArgs> FileUploaded;
-
- public void ProcessData()
- {
- for (int iCount = 0; iCount <= 10; iCount++)
- {
- Console.WriteLine("Data Processing is in progress .......");
- }
-
- UploadFile();
- }
-
- private void UploadFile()
- {
- Console.WriteLine("File Upload Completed");
- OnFileUploaded();
- }
-
-
-
-
- private void OnFileUploaded()
- {
- FileUploaded?.Invoke(this, new FileUploadedEventArgs() {FileLocation = "\\serverIpAddresss\tradingdata.xls"});
- Console.WriteLine("Event FileUploaded raised");
- }
- }
As we can see that the .NET framework in-built delegate publicdelegatevoidEventHandler<TEventArgs>(object sender, TEventArgs e) is associated with the event, a method with similar signature as delegate can be declared and assigned to the event.
- private static void StockExchange_FileUploaded(object sender, FileUploadedEventArgs e)
- {
- Console.WriteLine("StockExchange Class Event FileUploaded handled.");
- Console.WriteLine("File uploaded at location {0}", e.FileLocation);
- }
Output
Option 3
When there is a need to expose or make this event available to an application which does not understand or is incompatible with .NET Generics, then we can probably declare custom delegate instead of using .NET Generic delegates publicdelegatevoidEventHandler<TEventArgs>(object sender, TEventArgs e)
Let’s have a look at the example to understand how to declare custom delegate, raise an event and consume that event
Declare a custom delegate named FileUploadEventHandler
- public delegate void FileUploadEventHandler(Object sender, FileUploadedEventArgs args);
Now associate
FileUploadEventHandler delegate with the event. Let’s look at the implementation,
- public class StockExchange
- {
-
-
- public event FileUploadEventHandler FileUploaded;
-
- public void ProcessData()
- {
- for (int iCount = 0; iCount <= 10; iCount++)
- {
- Console.WriteLine("Data Processing is in progress .......");
- }
-
- UploadFile();
- }
-
- private void UploadFile()
- {
- Console.WriteLine("File Upload Completed");
- OnFileUploaded();
- }
-
-
-
-
- private void OnFileUploaded()
- {
- Console.WriteLine("Event FileUploaded raised");
- FileUploaded?.Invoke(this, new FileUploadedEventArgs() { FileLocation = "\\\\serverIpAddresss\\tradingdata.xls" });
- }
- }
As we can see the custom delegate publicdelegatevoidFileUploadEventHandler(Object sender, FileUploadedEventArgs args) is associated with the event, a method with a similar signature as delegate can be declared and assigned to the event.
- private static void StockExchange_FileUploaded(object sender, FileUploadedEventArgs e)
- {
- Console.WriteLine("StockExchange Class Event FileUploaded handled.");
- Console.WriteLine("File uploaded at location {0}", e.FileLocation);
- }
Output
Conclusion
Delegate is a type that defines a signature and holds a reference of method whose signature matches with the delegate.
Event is a notification raised by an object to signal the occurrence of an action. Delegate is associated with the event to hold a reference of a method to be called when the event is raised.
In the next article we will look at the Observer and Pub-Sub pattern which is based on the Event-Delegate Model.