Thursday, September 12, 2019

Understanding Delegates And Events In C# / .Net Simplified : C# Tutorial

Table Of Contents:

  • What is a Delegate?
  • C# Delegate Example.
  • A simple C# Delegate
  • What is Multicast Delegate in C#?
  • Purpose of Multicast Delegate in C#.
  • Purpose of Events in C#
  • Delegates and Asynchronous Method Calls in C#:
What are Delegates and Events in C#
What are Delegates and Events in C#
But What is the purpose of having delegates & Events in C#? If you understand the use of delegates,events then we can apply them in our daily projects.

What is a Delegate?

Lets forgot about C# for a while, a quick google search will tell a Delegate is a person who authorised to represent others. For example companies send their delegates to conferences to represent their products(because CEO might be busy with other things).
The CEO defines certain responsibilities to delegates (in advance)
  • What has to be done  (in programming world function)
  • What he should talk  (say parameter)
  • Collect feedback or get new customers (return value).
Now the CEO select one delegate for the conference when needed (say at run time)
Now if you read above definition a delegate can invoke a method which is having a same signature as that delegate. Confused??
I will try to explain the use of delegates & Events with simple C# examples so that at the end of this post you will have clear picture on Delegates and Events.

C# Delegate Example:

See the below example
//A simple C# Calculator class which will do arithmetic operations 
public class Calculator
    {
        public int Add(int x, int y){}
        public int Sub(int x, int y){}
        public int Multi(int x, int y){}
        public int Div(int x, int y){}
    }

Our client(say console application) will use above Calculator class to do maths operations. Console application will take two arguments
int operatorX = Convert.ToInt32(args[0]);
int operatorY = Convert.ToInt32(args[1]);
Calculator obj=new Calculator();
obj.Add(operatorX,operatorY);
obj.Sub(operatorX,operatorY);
obj.Multi(operatorX,operatorY);
obj.Div(operatorX,operatorY);

The Code looks fine and it will work perfectly but if you observe carefully client (console application) and our class are tightly coupled.
Say for example I want to calculate LCM (Least Common Multiple) of two numbers. We will add method in Calculator class and call that method in our client(Console application).
//Add method in Calculator Class
public LCM(int x,int y){}

// And in client sidere
obj.LCM(x,y);

Both are tightly coupled that means both Class and Client needs to be compiled in order to affect the changes.
This problem can be solved with the use of Delegates. Before that I will explain a simple delegate example and then will solve the above problem

A Simple C# Delegate:

Follow the below steps to create a Delegate
  • Define  : Define a Delegate
  • Create  : Create a Delegate object to assign
  • Refer or Point : Point to reference of a Method
  • Invoke : Finally Call or Invoke the delegate
Delegates definition syntax
delegate returntype delegateName (parameters);

For example see the below example.”SumDelegate” is a delegate which can refer functions which takes two integer parameters and returns one integer.
delegate int SumDelegate(int x,int y);

Create a Delegate Object
SumDelegate objDelgate=null;

We will use the above delegate to Call sum method which will add two numbers. In third steps we have to assign delegate to reference of a method
objDelegate=Sum;

And in the final step invoke the delegate
objDelegate.Invoke(x,y);

Final example
    //Define a Delegate
    delegate void SumDelegate(int x,int y);

    static void Main(string[] args)
    {
        //Declare Delegate Objetc
        SumDelegate objDelegate= null;

        //Point to method reference in our example "Sum"
        objDelegate=Sum;

        //Final Step Invoke Delegate
        objDelegate.Invoke(10, 20);
        Console.Read();
    }

    static void Sum(int x,int y)
    {
        Console.WriteLine((x+y).ToString());
    }

So What is the advantage? We can directly call the Sum method right why we have to use Delegate?
With the use of Delegates we call or assign methods at run time. How?
Going back to our problem we will slightly modify our Calculator as shown below
        //Declare Delegate
        public delegate int CalculatorDelegate(int x, int y);

        //Create Delegate Reference
        CalculatorDelegate delegateObj = null;

        //Depending upon request we will assign Delegate
        public CalculatorDelegate GetDelegateRef(int intoperation)
        {
            //And Finally assign based on request
            switch (intoperation)
            {
                case 1:
                    delegateObj = Add;
                    break;
                case 2:
                    delegateObj = Sub;
                    break;
                case 3:
                    delegateObj = Multi;
                    break;
                case 4:
                    delegateObj = Div;
                    break;
                default: break;
            }
            return delegateObj;
        }
        private int Add(int x, int y){}
        private int Sub(int x, int y){}
        private int Multi(int x, int y){}
        private int Div(int x, int y){}

We Declare a Delegate “CalculatorDelegate” which takes two int parameters and returns int.This delegate can refer methods like Add,Sub,Multi,Div etc.
public CalculatorDelegate GetDelegateRef(int intoperation){}

The above method GetDelegateRef() is used to get delegate reference i.e., which method to call(Add,Sub,Multi etc)  based upon request from clients.
We will modify our client code slightly as shown below
int intoperation = Convert.ToInt32(args[0]);
int operatorX = Convert.ToInt32(args[1]);
int operatorY = Convert.ToInt32(args[2]);

Calculator obj=new Calculator();

Console client will take three arguments first argument decides which operation to perform and 2nd , 3rd arguments are used for calculations.
And for invoking
obj.GetDelegateRef(intoperation).Invoke(operatorX, operatorY);
With the use of GetDelegateRef() method at run time we will know which method to call and then Invokes the delegate.
Now if you want to Add LCM operation simply add it to Calculator Class (if intoperation=5 assign delegate to LCM method) and no need to change any code at client side, at run time we will decide which operation to invoke. No need to compile client code.
That means we are decoupling client from our Class with the help of Delegates. Whenever we introduce any features try to avoid changes at client side

Multicast Delegate in C# :

In the above example the delegate object pointing to single function or method at a time.
But delegate object has the capability of holding multiple method references and whenever we invoke delegate object it will call those methods one by one. That is Called “Multicast delegate”(Naming convention only).
We will be having only a Delegate depending upon its usage we call it either Delegate or Multicast Delegate. See the below example
CalculatorDelegate delegateObj = null;
delegateObj +=Add;
delegateObj +=Sub;
delegateObj +=Multi;

//Performs Add,Sub,Multi one by one.
delegateObj.Invoke(10,20);

Purpose Of Multicast Delegate in C#:

So where we  can use multicast delegates in our daily projects.Everyone will have Facebook account rite. Whenever we get any notifications we will be receiving SMS and as well as emails.
Facebook will publish notifications and we will receive notifications to Mobile and Email.This is like publisher and subscriber Model.
publisher And SubscriberDelegate
Publisher And Subscriber Delegate
In this kind of publisher & Subscribers model we can use Multicast Delegates. We will go through one simple example to understand it better.
//Subscribers
    public class SendViaMobile
    {
        public void sendMessage(string msg)
        {
            Console.WriteLine("Send to Mobile"+msg);
        }
    }
    public class SendViaEmail
    {
        public void sendEmail(string msg)
        {
            Console.WriteLine("Send to EMail"+msg);
        }
    }

    //Publisher
    public class Publisher
    {
        //Declare Delegate
        public delegate void PublishMessageDel(string msg);

        //Define Delegate
        public PublishMessageDel publishmsg = null;
        //Method used to Invoke Delegate
        public void PublishMessage(string message)
        { 
            //Invoke Delegate
            publishmsg.Invoke(message);
        }
    }

The Code is easy to Understand I declared two Subscribers “SendViaEmail” and “SendViaSMS”. And in publisher class I declared one Delegate which takes string parameter and returns void.
And For invoking delegate I wrote one more method called “PublishMethod”  (You will understand why I wrote this method at the end of this post)
And To test the application we will write one Console Application
Publisher publisher = new Publisher();

SendViaEmail SE = new SendViaEmail();
SendViaMobile SM = new SendViaMobile();

publisher.publishmsg += SE.sendEmail;
publisher.publishmsg += SM.sendMessage;

publisher.PublishMessage("Hello You Have received New Notification");

But the problem with the above code is it’s more over like “Master and Slave model”.
That means You are not subscribed to Mobile notifications but still Facebook sending notifications to your mobile.
In the above code subscribers do not have rights to decline notifications.Publisher only sending notifications irrespective of subscriber interests.This is not ideal Publisher/Subscriber Model. First Subscribers has to subscribe for notification then only it should receive notifications.
So we will slightly modify above code to look like Publisher/Subscriber Model.
public class SendViaMobile
{
        private void sendMessage(string msg)
        {
            Console.WriteLine("Send to Mobile:" + msg);
        }
        public void Subscribe(Publisher pub)
        {
            pub.publishmsg += sendMessage;
            //pub.publishmsg = null;
        }
}

I added one method called Subscribe() which takes Publisher Object as parameter and decides whether to receive notifications or not.Subscribe Method assigns Delegate to corresponding methods(I declared Method as private).And it can assign any number of methods to delegate like For example “SendViaMobile” wants to save the notifications in Log files So internally it can define one method which is same signature as Delegate and assign it to Delegate Object.
So in our console application we have to call subscribe method before invoking the delegate.
Publisher publisher = new Publisher();

SendViaMobile SM = new SendViaMobile();
SendViaEmail SE = new SendViaEmail();

//Subscribing for Mobile notifications
SM.Subscribe(publisher);

//Emails are not subscribed so it wont receive notifications via Email
//SE.Subscribe(publisher);

//Invoking the delegate Only Mobile will receive notifications.
publisher.PublishMessage("Hello You Have New Notifications");

“SendViaEmail” not subscribed(I put it in comments) to notifications so it wont receive any notifications.
So far everything is Good But here we are ignoring one serious issue “We are exposing Delegate to Subscribers” So what ? what happens if we expose to subscribers?
In the the above code “SendViaMobile” class, In subscribe method I wrote one Line
//pub.publishmsg = null;
public void Subscribe(Publisher pub)
{
            pub.publishmsg += sendMessage;
            //pub.publishmsg = null;
}

Just remove the comment line that means we assigning delegate object to ‘null’
And in our client code
Publisher publisher = new Publisher();
SendViaMobile SM = new SendViaMobile();
SendViaEmail SE = new SendViaEmail();
SE.Subscribe(publisher);
SM.Subscribe(publisher);
publisher.PublishMessage(“Hello You have new Notifications”);
First I subscribed Email and then mobile. But In Mobile Subscribe class we assigned delegate to null. That means even Email also do not receive any notifications.
And also Subscriber can invoke our delegate.
C# Delegate Problem
C# Delegate Problem

that means we are violating Encapsulation principle
So What is the solution?
  • We should not expose delegate to subscribe (In that case it will become Master Slave Model)
  • If we expose also we should not give permissions to subscriber to change or invoke delegate.That means it should only assign some methods or functions to Delegate.
The Second solution seems to be good right. That’s how Events Came into Picture.

The purpose of Events in C#:

As explained above Events can solve the problem of exposing delegates to subscribers to achieve ideal Publisher/Subscriber Model.
See the below example
public delegate void PublishMessageDel(string msg);
public event PublishMessageDel publishevent=null;

//Just Add "event" keyword before delegate object declaration

The invoking and everything is same as above
And if want to change  or invoke delegate from other classes it will throw following error
Delegate and Events in C#
Delegate and Events in C#
That means we cannot invoke or change the delegate event in outside classes other than Publisher Class. That’s the reason I wrote PublishMessage() method in Publisher class. This method will invoke the delegate event internally. The outside classed can call PublishMessage() method to invoke delegate.
  • Events are invoked in within the class itself.
  • Outside classes do not have access to invoke the Delegate Event.
  • It can appear only on left hand side of += or -= that means only appending or removing allowed No assignment.
That means Event will hide the sensitive data of Delegate and exposes necessary data to outside world.This is the ideal example of Encapsulation principle in OOPS.
I hope you understand the difference between Delegates and Events in C#.
One more use of Delegates is it’s used to call methods asynchronously.

Delegates and Asynchronous Method Calls:

What are Asynchronous method calls? Some times we need to perform a large task. But we don’t want to wait till the task ends so that we can perform other tasks.
And whenever the large task ends we need to receive a notification whether it is success or not. In real world we call this as asynchronous execution of task.
We can easily achieve this with the use of Delegates.
public class AsynchDelegate
    {
        delegate string AsynchDel();
        public static void Main(string[] args)
        {
            AsynchDel delegateObj = LargeTask;
            delegateObj.BeginInvoke(new AsyncCallback(CallBackLargeTask), delegateObj);
            Console.WriteLine("New task is started!");
            Console.Read();
        }
        public static void CallBackLargeTask(IAsyncResult asyncResult)
        {
            AsynchDel doLargeTask = (AsynchDel)asyncResult.AsyncState;
            string message = doLargeTask.EndInvoke(asyncResult);
            Console.WriteLine(message);
        }
        public static string LargeTask()
        {
            Thread.Sleep(3000);
            return "success";
        }
    }

The code is straight forward and easy to understand
  • Assigned LargeTask() to delegate object.
  • Invoke delegate using .BeginInvoke()
  • First parameter is callback mechanism(In our case ‘CallBackLargeTask’) and second one is delegate object.
  • Callback mechanism is responsible for sending notification back to client.
  • We will use .EndInvoke() Method to get the success message.

No comments:

Post a Comment

Get max value for identity column without a table scan

  You can use   IDENT_CURRENT   to look up the last identity value to be inserted, e.g. IDENT_CURRENT( 'MyTable' ) However, be caut...