Showing posts with label Delegates. Show all posts
Showing posts with label Delegates. Show all posts

Saturday, September 12, 2020

Events and Delegates

  • Events is a mechanism for communication between objects. When something happened to an object, It can notify other objects about that. 
  • Events are used to build a loosely couple application which classes and components are not tightly couple together. 
  • It helps to extend the application without breaking and changing the existing functionality. 
  • For an example, suppose we have a class  "VideoEncoder" which used to encode the video and send the email once video is encoded. In future, we might need to add another functionality  "send the text message" after the video is encoded. So then we have to add another functionality to send the messages. The problem is, we have to recompile the encoder class and all dependents of encoder class and redeploy. This is not a good idea. 

  • To solve this, we can use events. Here VideoEncoder is the publisher. mailService and messageService  are the subscribers. Video encoder does not know anything about the mailService and messageService. So in that case, in future if we need to add another subscriber , we can do it with minimal impact.  
       There are three main steps to implement above solution. 
  1. Define the delegate - Delegates works as a Agreement or contract between publisher and subscriber. It determines the signature of the event handler method in subscriber. 
  2. Define the Event based on that delegate.  
  3. Raise the event method which is protected ,virtual and void. 
    //By creating custom delegate and  EventArgs.Empty
        public class VideoEncoder
        {
            //1.Defines delegate
            //2.Defines a event based on event
            //3.raise the event
    
            public delegate void VideoEncodedEventHandler(object source, EventArgs args);
            public event VideoEncodedEventHandler VideoEncoded;
    
            public void Encode(Video video)
            {
                Console.WriteLine("Video Encoded");
                Thread.Sleep(3000);
    
                OnvideoEncoded();
            }
    
            protected virtual void OnvideoEncoded()
            {
                //VideoEncoded?.Invoke(this, EventArgs.Empty);
    
                if (VideoEncoded != null)
                {
                    VideoEncoded(this, EventArgs.Empty);
                }
    
            }
    
        }
    	
        class Program
        {
            static void Main(string[] args)
            {
                var video = new Video() { Name = "video1" };
                var videoEncoder = new VideoEncoder(); //publisher
                var mailService = new MailService();//Subscriber
                var messageService = new MessageService(); //Subscriber
    
                videoEncoder.VideoEncoded += mailService.OnVideoEncoded;
                videoEncoder.VideoEncoded += messageService.OnVideoEncoded;
                videoEncoder.Encode(video);
    	}
         }
    	
        public class MailService
        {
            public void OnVideoEncoded(object source, EventArgs args)
            {
                Console.WriteLine("Sending the email");
            }
        }
    	
    	public class MessageService
        {
            public void OnVideoEncoded(object source, EventArgs args)
            {
                Console.WriteLine("Sending the message");
            }
        }
        
        public class Video
        {
            public string Name { get; set; }
        }
    
  • Suppose we need to send addtional data (such as the video name which is encoded) to the subscriber. To do that, instead of EventsArgs we can use a custom class which derives from "EventArgs" class.
  •     //By creating custom  VideoEventArgs
        public class VideoEncoder
        {
            public delegate void VideoEncodedEventHandler(object source, VideoEventArgs args);
            public event VideoEncodedEventHandler VideoEncoded;
    
            public void Encode(Video video)
            {
                Console.WriteLine("Video Encoded");
                Thread.Sleep(3000);
    
                OnvideoEncoded(video);
            }
    
            protected virtual void OnvideoEncoded(Video video)
            {
                // VideoEncoded?.Invoke(this, new VideoEventArgs() { Video = video }); or
    
                if (VideoEncoded != null)
                {
                    VideoEncoded(this, new VideoEventArgs() { Video = video });
                }
    
            }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var video = new Video() { Name = "PickUpLimes" };
                var videoEncoder = new VideoEncoder(); //publisher
                var mailService = new MailService();//Subscriber
                var messageService = new MessageService(); //Subcriber
    
                videoEncoder.VideoEncoded += mailService.OnVideoEncoded;
                videoEncoder.VideoEncoded += messageService.OnVideoEncoded;
                videoEncoder.Encode(video);
                Console.Read();
            }
        }
    
        public class VideoEventArgs : EventArgs
        {
            public Video Video { get; set; }
        }
    
        public class MailService
        {
            public void OnVideoEncoded(object source, VideoEventArgs args)
            {
                Console.WriteLine("Sending the email. Video name is " + args.Video.Name);
            }
        }
    
        public class MessageService
        {
            public void OnVideoEncoded(object source, VideoEventArgs args)
            {
                Console.WriteLine("Sending the message. Video name is " + args.Video.Name);
            }
        }
    
        public class Video
        {
            public string Name { get; set; }
        }
    
  • In .Net , same thing can be achieved using in-built EventHandler delegate instead of creating the custom delegate. EventHandler comes in two forms. One is normal form EventHandler  and other one is generic form EventHandler<T> . If we need to pass the data, we can use generic version. If not , can use the normal form.   
  •  	
        //using EventHandler delegate instead of custom delegate
        public class VideoEncoder
        {
            //Generic Form
            public event EventHandler<VideoEventArgs> VideoEncoded;
    
            //Normal Form
           // public event EventHandler VideoEncoded; 
    
            public void Encode(Video video)
            {
                Console.WriteLine("Video Encoded");
                Thread.Sleep(3000);
    
                OnvideoEncoded(video);
            }
    
            protected virtual void OnvideoEncoded(Video video)
            {
                //VideoEncoded?.Invoke(this, EventArgs.Empty);
    
                if (VideoEncoded != null)
                {
                    VideoEncoded(this, new VideoEventArgs() { Video = video });
                }
    
            }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var video = new Video() { Name = "PickUpLimes" };
                var videoEncoder = new VideoEncoder(); //publisher
                var mailService = new MailService();//Subscriber
                var messageService = new MessageService(); //Subcriber
    
                videoEncoder.VideoEncoded += mailService.OnVideoEncoded;
                videoEncoder.VideoEncoded += messageService.OnVideoEncoded;
                videoEncoder.Encode(video);
                Console.Read();
            }
        }
    
        public class VideoEventArgs : EventArgs
        {
            public Video Video { get; set; }
        }
    
        public class MailService
        {
            public void OnVideoEncoded(object source, VideoEventArgs args)
            {
                Console.WriteLine("Sending the email. Video name is " + args.Video.Name);
            }
        }
    
        public class MessageService
        {
            public void OnVideoEncoded(object source, VideoEventArgs args)
            {
                Console.WriteLine("Sending the message. Video name is " + args.Video.Name);
            }
        }
    
        public class Video
        {
            public string Name { get; set; }
        }
    

 References : Udemy.com

Thursday, September 10, 2020

Delegates

  • Delegate is an object that knows how to call a method or a  group of methods which have the same signature. 
  • It is a reference to a function.
  • Of course, we can call methods directly, but we need delegates for designing extensible and flexible applications. (Ex: Frameworks).
  • Imagine that you are designing a framework which is use for processing the photos. In that framework, you have defined few photo processing ways such as "Apply Brightness" , "Apply Contrast" and etc. In future, may be a client who is using your framework need to add their own photo filter which we haven't defined. So in that case, we have to change our application , recompile and redeploy. It is not a good idea. If we use delegates in our frameworks, client can add any no of filters without relying on us. See below example. 
  •     public class Photo
        {
            public Photo Load(string path)
            {
                return new Photo();
            }
        }
    	
        public class Filter
        {
            public void Resize(Photo photo)
            {
                Console.WriteLine("Changed the Size");
            }
    
            public void ChangeBrightness(Photo photo)
            {
                Console.WriteLine("Changed the brightness");
            }
    
            public void ChangeContrast(Photo photo)
            {
                Console.WriteLine("Changed contrast of the photo");
            }
    
        }
    	
        public class PhotoMaker
        {
            // Define the delegate
            public delegate void PhotoFilterHandler(Photo photo);
            public void Process(string path, PhotoFilterHandler photofilterhandler )
            {
                var photoObject = new Photo();
                var photo = photoObject.Load(path);
                photofilterhandler(photo);
            }
        }
    	
        class Program
        {
            static void Main(string[] args)
            {
                var photomaker = new PhotoMaker();
                var filter = new Filter();
    			
    	    // Adding ChangeBrightness filter
                PhotoMaker.PhotoFilterHandler filterhandler = filter.ChangeBrightness;
    			
    	    //Adding ChangeContrast filter
    	    filterhandler += filter.ChangeContrast;
    			
    	    // Remove ChangeBrightness filter
                filterhandler -= filter.ChangeBrightness;
    			
    	    //Adding custom method  without changing the PhotoProcess class
                filterhandler+= RemoveRedEye;
               
    	    photomaker.Process("", filterhandler);	
    
                Console.Read();
            }
    
            static void RemoveRedEye(Photo photo)
            {
                Console.WriteLine("Remove Red Eye");
            }
        }
    	
    
  • In .Net, there are two delegates which are generic. They are Action and Func. Difference between Func and Action is Func point to a method that has a return value and Action point to a method that returns void (no return type). 

  • Below code uses in built Action delegate instead of creating a custom delegate. 
  •  	
    public class Photo
        {
            public Photo Load(string path)
            {
                return new Photo();
            }
        }
    	
        public class Filter
        {
            public void Resize(Photo photo)
            {
                Console.WriteLine("Changed the Size");
            }
    
            public void ChangeBrightness(Photo photo)
            {
                Console.WriteLine("Changed the brightness");
            }
    
            public void ChangeContrast(Photo photo)
            {
                Console.WriteLine("Changed contrast of the photo");
            }
        }
    	
        public class PhotoMaker
        {
            public void Process(string path, Action<Photo> photofilterhandler )
            {
                var photoObject = new Photo();
                var photo = photoObject.Load(path);
                photofilterhandler(photo);
            }
        }
    	
        class Program
        {
            static void Main(string[] args)
            {
                var photomaker = new PhotoMaker();
                var filter = new Filter();
    			
                // Adding ChangeBrightness filter
                Action<Photo> filterhandler = filter.ChangeBrightness;
    			
    	    //Adding ChangeContrast filter
    	    filterhandler += filter.ChangeContrast;
    			
    	    // Remove ChangeBrightness filter
                filterhandler -= filter.ChangeBrightness;
    			
    	    photomaker.Process("", filterhandler);	
    
                Console.Read();
            }
        }

  • Use a delegate in the following circumstances:
      • An eventing design pattern is used.
      • The caller has no need to access other properties, methods, or interfaces on the object implementing the method.