Delegates and Events In C# .NET

Delegates and Events In C# .NET

The concept of events and delegates is a little bit confusing for some of us. I was one of them when I started.

I hope by the end of this article you have a good and in-depth understanding of Events and Delegates, why we need them, how to use them, and how to create them.

?

The Event is a mechanism for communication between objects. This means when something happens inside an object, it can notify other objects about that. we can benefit from that while building loosely coupled applications.

What is a loosely coupled application? It's an application that which its components or classes are not tightly coupled together. loosely coupled application is easy to extend without breaking the code.


Ex: if sending an email to the owner of the video after the encoding process.

public class VideoEncoder
{
    public void Encode(Video video)
    {
        //Encoding Logic
        // ...
        _mailService.Send(new Mail());
    }
}        

Nothing wrong with this code but in the future if I need to send a message to the owner of the video I have to edit the original code.

Of course, this is for simplicity but in real world scenario, it could be an extra line of code.

public class VideoEncoder
{
    public void Encode(Video video)
    {
        //Encoding Logic
        // ...
        _mailService.Send(new Mail());
        _messageService.Send(new Text());
    }
}        

The problem here is that we added these two extra lines when that Encode method changed which means it has to be recompiled so the VideoEncoder class also has to be recompiled which means any other classes that are dependent on VideoEncoder have to be recompiled and redeployed.

The problem here with this code by adding that extra line may break by accidentally something along the way.


As a software engineer, you want to reduce this problem by designing the application such that when you want to change the application that change has minimal impact on the overall application.


Now we can use events to solve this problem. We can make the VideoEncoder class publish (event sender) an event and make the MailService class subscribe (event receiver) to this event.

What is interesting about that is the VideoEncoder knows absolutely nothing about the MailService which means in the future if we want to extend our application and add the capability to send a message when the video is encoded simply create a new class called MessageService that has to subscribe to that event on the VideoEncoder.

So basically the VideoEncoder does not need to be recompiled and redeployed and simply extends the application by adding a new class so this reduces the impact on the application.


So let's take a look if we want to implement this events mechanism between classes:

First, we need to introduce a new concept called a delegate. The delegate is an agreement/contract between the publisher and the subscriber. It determines the signature of the event handler method in the subscriber. We will clarify by code example in seconds.


#Steps:

1) define a delegate.

2) Define an event based on the delegate.

3) Raise the event.


let's create the MediaEncoder class as a publisher:

namespace EventsAndDelegates
{
    internal class MediaEncoder
    {

        //1) define a delegate
        public delegate void AudioEncodedEventHandler(object source, EventArgs args);
        
        //2) Define an event based on the delegate
        public event AudioEncodedEventHandler? AudioEncoded;
        

        public void Encode()
        {
            Console.WriteLine("Encoding video ...");
            Thread.Sleep(3000);

            //Assume we will notify all the subscribers
            OnAudioEncoded();
        }


        //3) Raise the event
        protected virtual void OnAudioEncoded()
        {
            //#If any subscribers raise the event
            if (AudioEncoded != null)
                AudioEncoded(this, EventArgs.Empty);
        }
    }
}        

Notice that:

  • source parameter => the publisher of the event (MediaEncoder class).
  • args parameter => any additional data want to send with the event.
  • EventArgs.Empty => means we don't need to send any data with the event (null).
  • the .net suggests that the event publisher method that's used to raise the event should be => protected virtual void.


Naming Convention:

  1. AudioEncodedEventHandler delegate: the name of the event (AudioEncoded) then append (EventHandler) word.
  2. AudioEncoded event: the name of the event (AudioEncoded) remove (EventHandler) word.
  3. OnAudioEncoded method: the name of the event (AudioEncoded) Preceded with (On) word for the event publisher method.


let's create two classes MailService and MessageService as subscribers:

namespace EventsAndDelegates
{
    internal class MailService
    {
        public void OnAudioEncoded(object source, EventArgs args)
        {
            Console.WriteLine("MailService: sending an email...");
        }
    }
}        
namespace EventsAndDelegates
{
    internal class MessageService
    {
        public void OnAudioEncoded(object source, EventArgs args)
        {
            Console.WriteLine("MessageService: Sending a text message...");
        }
    }
}        


Now we need to subscribe to the event (register a subscriber) as shown in the program.cs:

using EventsAndDelegates;

var mediaEncoder = new MediaEncoder(); //publisher
var mailService = new MailService(); //subscriber
var messageService = new MessageService(); //subscriber

mediaEncoder.AudioEncoded += mailService.OnAudioEncoded;
mediaEncoder.AudioEncoded += messageService.OnAudioEncoded;

mediaEncoder.Encode();        

mailService.OnAudioEncoded references or points for that method OnAudioEncoded at MailService. So mediaEncoder.AudioEncoded event behind the seen is a list of pointers' methods.


Now to the next level if we want to send additional data when raising the event:

let's repeat the same example code with passing additional data.

we need to create a new class (VideoEventArgs) that is inherited from the EventArgs class and then pass the data on it:

namespace EventsAndDelegates;

internal class VideoEventArgs : EventArgs
{
    public Video? Video { get; set; }
}

internal class Video
{
    public string? Title { get; set; }
}         


Now let's rewrite the code:

let's create the MediaEncoder class as a publisher:

namespace EventsAndDelegates
{
    internal class MediaEncoder
    {
        //1) define a delegate
        public delegate void VideoEncodedEventHandler(object source, VideoEventArgs args);

        //2) Define an event based on the delegate
        public event VideoEncodedEventHandler? VideoEncoded;

        public void Encode(Video video)
        {
            Console.WriteLine("Encoding video ...");
            Thread.Sleep(3000);

            //Assume we will notify all the subscribers
            OnVideoEncoded(video);
        }

        //3) Raise the event
        protected virtual void OnVideoEncoded(Video video)
        {
            if (VideoEncoded != null)
                VideoEncoded(this, new VideoEventArgs() {Video = video});
        }
    }
}        


let's create two classes MailService and MessageService as subscribers:

namespace EventsAndDelegates
{
    internal class MailService
    {
        public void OnVideoEncoded(object source, VideoEventArgs e)
        {
            Console.WriteLine("MailService: sending an email..." + e.Video?.Title);
        }
    }
}        
namespace EventsAndDelegates
{
    internal class MessageService
    {
        public void OnVideoEncoded(object source, VideoEventArgs e)
        {
            Console.WriteLine("MessageService: Sending a text message..." + e.Video?.Title);
        }
    }
}        


Now we need to subscribe to the event (register a subscriber) as shown in the program.cs:

using EventsAndDelegates;

var video = new Video() { Title = "Video 1"};
var mediaEncoder = new MediaEncoder(); //publisher
var mailService = new MailService(); //subscriber
var messageService = new MessageService(); //subscriber

mediaEncoder.VideoEncoded += mailService.OnVideoEncoded;
mediaEncoder.VideoEncoded += messageService.OnVideoEncoded;

mediaEncoder.Encode(video);        


Finally, I want to mention that there's a shorter way to create the delegate and event:

1) EventHandler: if we don't need to send any data with the event.

2) EventHandler<TEventArgs>: send additional data


Let's rewrite the MediaEncoder for handling the AudioEncoded event:

namespace EventsAndDelegates
{
    internal class MediaEncoder
    {
        public event EventHandler? AudioEncoded;

        public void Encode()
        {
            Console.WriteLine("Encoding video ...");
            Thread.Sleep(3000);

            OnAudioEncoded();
        }

        protected virtual void OnAudioEncoded()
        {
            if (AudioEncoded != null)
                AudioEncoded(this, EventArgs.Empty);
        }
    }
}        


Let's rewrite the MediaEncoder for handling the VideoEncoded event:

namespace EventsAndDelegates
{
    internal class MediaEncoder
    {
        public event EventHandler<VideoEventArgs>? VideoEncoded;

        public void Encode(Video video)
        {
            Console.WriteLine("Encoding video ...");
            Thread.Sleep(3000);

            OnVideoEncoded(video);
        }

        protected virtual void OnVideoEncoded(Video video)
        {
            if (VideoEncoded != null)
                VideoEncoded(this, new VideoEventArgs() {Video = video});
        }
    }
}        


要查看或添加评论,请登录

Mohamed Samy的更多文章

  • Cross-Site Request Forgery (CSRF) Attack

    Cross-Site Request Forgery (CSRF) Attack

    What is CSRF? CSRF attacks occur when a malicious website tricks a user's browser into making unintended requests to a…

  • The Hi/Lo Algorithm

    The Hi/Lo Algorithm

    The Hi/Lo Algorithm is also known as the High-Low Algorithm. it is useful for generating unique keys for entities…

  • Asymmetric Encryption

    Asymmetric Encryption

    Encryption is the process of taking a message and scrambling its contents so that only certain people can look at your…

  • The Purpose of Asynchronous Code

    The Purpose of Asynchronous Code

    Writing async code on the server is all about freeing up threads as soon as possible so they can be used for other…

  • CAP Theorem

    CAP Theorem

    What is the CAP theorem? How useful is it to system design? Let’s take a look. The CAP theorem is a concept in computer…

    1 条评论
  • Common Language Runtime (CLR) in .Net

    Common Language Runtime (CLR) in .Net

    Before C# we have to language in the C family C/C++. Using C or C++ our application compiler translated our code into…

    2 条评论
  • Difference between System Design and System Architecture

    Difference between System Design and System Architecture

    System design and system architecture are related concepts in the field of software and systems engineering, but they…

社区洞察

其他会员也浏览了