Delegates and Events in C#

Delegates and Events in C#
A Delegate is basically function pointer. A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked.

Simple No Delegate Example:

using System;
 
namespace NoDelegate
{
    public class 
MySimpleClass
    {
        public void 
Method1()
        {
            Console.WriteLine("Method() begin");
            Console.WriteLine("Method() end");
        }
    }
 
    public class MyAnotherClass
    {
        static void Main(string[] args)
        {
            
MySimpleClass myClass = new MySimpleClass();

            myClass.
Method1();
        }
    }
}
In most cases, when we call a function, we specify the function to be called directly. As we called in above snippet myClass.Method1().

Delegate Basics
An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method's argument types and return type match the delegate's. This makes delegates perfectly suited for "anonymous" invocation.
The signature of a delegate is shown below:
delegate result-type identifier ([parameters]);
where:
  • result-type: The result type, which matches the return type of the function.
  • identifier: The delegate name.
  • parameters: The Parameters, that the function takes.
The declaration for a delegate looks just like the declaration for a function, except that in this case, we're declaring the signature of functions that this delegate can reference.
There are three steps in defining and using delegates:
  • Declaration
  • Instantiation
  • Invocation

Simple Delegate Example 1:

using System;

namespace MySimpleDelegate
{
    
// Step 1: Declaration
    public delegate void 
SimpleDelegate();

    class MyDelegateClass
    {
        public static void 
MyFunc()
        {
            Console.WriteLine("I was called by delegate ...");
        }

        public static void Main()
        {
            
// Step 2: Instantiation
            
SimpleDelegate simpleDelegate = new SimpleDelegate(MyFunc);

            
// Step 3: Invocation
            simpleDelegate();
        }
    }
}
Bit a complex example of delegate that takes a single parameter and has no return type.

using System;

namespace MySimpleDelegate
{
    
// Delegate Specification
    public class MyDelegateClass
    {
        
// Step 1: Declaration
        public delegate void LogHandler(string message);

        public void Process(
LogHandler logHandler)
        {
      // Step 3: Invocation (Checking for null)
            if (logHandler != null)
            {
                logHandler("Process() begin");
            }

            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }

 public class MyTestApplication
 {
  
// Static Function: To which is used in the Delegate. To call the Process()
  // function, we need to declare a logging function: Logger() that matches
  // the signature of the delegate.

  static void Logger(string s)
  {
      Console.WriteLine(s);
  }

  static void Main(string[] args)
  {
     MyDelegateClass myClass = new MyDelegateClass();

      // Step 2: Instantiation of delegate, pointing to the logging function.
      // This delegate will then be passed to the Process() function.

      MyClass.
LogHandler myLogger = new MyClass.LogHandler(Logger);
      myClass.Process(
myLogger);
  }
  }
}

Simple Delegate Example 2:

using System;
using System.IO;

namespace MySimpleDelegate
{
    
    public class MyDelegateClass
    {
        
// Step 1: Declaration
        public delegate void 
LogHandler(string message);


        public void Process(LogHandler logHandler)
        {
     // Step 3: Invocation (Checking for null)
            if (logHandler != null)

            {
                logHandler("Process() begin");
            }

            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }

    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        
// Constructor
        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        
// Member Function which is used in the Delegate
        public void 
Logger(string s)
        {
            streamWriter.WriteLine(s);
        }

        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }

    public class MyTestApplication

    {
        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");

            MyDelegateClass myClass = new MyDelegateClass();

            // Step 2: instance of the delegate, pointing to the Logger()
            // function on the fl instance of a FileLogger.

            MyClass.
LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
            myClass.Process(myLogger);
            fl.Close();
        }
    }
}
The cool part in above 2 simple examples is that we didn't have to change the Process() function; the code to all the delegate is the same regardless of whether it refers to a static or member function.



In C#, delegates are multicast, which means that they can point to more than one function at a time (that is, they're based off the System.MulticastDelegate type). A multicast delegate maintains a list of functions that will all be called when the delegate is invoked. We can add back in the logging function from the first 2 simple examples, and call both delegates. Here's what the code looks like:

using System;
using System.IO;

namespace MySimpleDelegate
{
    // Delegate Specification
    public class MyDelegateClass
    {
        
// Step 1: Declaration
        public delegate void 
LogHandler(string message);

        public void Process(
LogHandler logHandler)
        {
            if (logHandler != null)
            {
                logHandler("Process() begin");
            }

            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }

    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        // Member Function used in the Delegate
        public void Logger(string s)
        {
            streamWriter.WriteLine(s);
        }


        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }


    public class TestApplication
    {
        
 // Static Function used in the Delegate
        static void Logger(string s)
        {
            Console.WriteLine(s);
        }


        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");

            MyClass myClass = new MyClass();

            // Step 2: instance of the delegate
            MyClass.LogHandler myLogger = null;
            myLogger 
+= new MyClass.LogHandler(Logger);
            myLogger 
+= new MyClass.LogHandler(fl.Logger);

            myClass.Process(myLogger);
            fl.Close();
        }
    }
}



Events

An event is a member that enables a class or object to provide notifications. An event is declared like a field except that the declaration includes an event keyword and the type must be a delegate type.
The following important conventions are used with events:
  • Event Handlers in the .NET Framework return void and take two parameters.
  • The first paramter is the source of the event; that is the publishing object.
  • The second parameter is an object derived from EventArgs.
  • Events are properties of the class publishing the event.
  • The keyword event controls how the event property is accessed by the subscribing classes.

Let's modify our logging example from above to use an event rather than a delegate:


using System;
using System.IO;

namespace MySimpleEvent
{
    
/* ========= Event Class ============== */
    public class MyEventClass
    {
        
// Step 1: Declaration
        public delegate void 
LogHandler(string message);

       
 // Define an Event based on the above Delegate
        public event 
LogHandler Log;
 
 
        
// Instead of having the Process() function take a delegate
        // as a parameter, we've declared a Log event. Call the Event,
        // using the OnXYZ Method, where XYZ is the name of the Event.

        public void Process()
        {
            
OnLog("Process() begin");
            
OnLog("Process() end");
        }

        
// By Default, create an OnXYZ Method, to call the Event
        protected void 
OnLog(string message)
        {
            if (
Log != null)
            {
                
Log(message);
            }
        }
    }

   
 // The FileLogger class merely encapsulates the file I/O
    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        
// Constructor
        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        
// Member Function which is used in the Delegate
        public void Logger(string s)
        {
            streamWriter.WriteLine(s);
        }

        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }

    /* ========= Observer of the Event ============== */
    
// It's now easier and cleaner to merely add instances
    // of the delegate to the event, instead of having to
    // manage things ourselves

    public class MyTestApplication
    {
        static void Logger(string s)
        {
            Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");
            MyEventClass myClass = new MyEventClass ();

            // Subscribe the Functions Logger and fl.Logger
            myClass.Log += new MyClass.LogHandler(Logger);
            myClass.Log += new MyClass.LogHandler(fl.Logger);

            // The Event will now be triggered in the Process() Method
            myClass.Process();


            fl.Close();
        }
    }
}


Another example of Event

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SecondChangeEvent
{
    // Our subject -- it is this class that other classes
    // will observe. This class publishes one event:
    // SecondChange. The observers subscribe to that event.
    public class Clock
    {
        private int _hour;
        private int _min;
        private int _sec;

        // The delegate named SecondChangeHandler, which will encapsulate
        // any method that takes a clock object and a TimeInfoEventArgs
        // object as the parameter and returns no value. It's the
        // delegate the subscribers must implement.
        public delegate void SecondChangeHandler(object clock, TimeInfoEventArgs timeInfo);

        public event SecondChangeHandler SecondChange;


        protected void onSecondChange(object clock, TimeInfoEventArgs timeInfo)
        {
            if (SecondChange != null)
            {
                SecondChange(clock, timeInfo);
            }
        }

        public void Run()
        {
            for (; ; )
            {
                Thread.Sleep(1000);

                DateTime dt = DateTime.Now;

                if (dt.Second != _sec)
                {
                    TimeInfoEventArgs timeInfo = new TimeInfoEventArgs(dt.Hour, dt.Minute, dt.Second);

                    onSecondChange(this, timeInfo);
                }
           

               _hour= dt.Hour;
               _min = dt.Minute;
               _sec = dt.Second;
           
            }
       
        }

    }


    // The class to hold the information about the event
    // in this case it will hold only information
    // available in the clock class, but could hold
    // additional state information
    public class TimeInfoEventArgs : EventArgs
    {
        public readonly int hour;
        public readonly int min;
        public readonly int sec;
       
        public TimeInfoEventArgs(int hour, int min, int sec)
        {
            this.hour = hour;
            this.min = min;
            this.sec = sec;
        }
    }

    // An observer. DisplayClock subscribes to the
    // clock's events. The job of DisplayClock is
    // to display the current time
    public class DisplayClock
    {
        // Given a clock, subscribe to
        // its SecondChangeHandler event
        public void Subscribe(Clock theClock)
        {
            theClock.SecondChange += new Clock.SecondChangeHandler(TimeHasChanged);
        }

        // The method that implements the delegated functionality
        public void TimeHasChanged(object theClock, TimeInfoEventArgs ti)
        {
            Console.WriteLine("Current Time: {0}:{1}:{2}",
                                                           ti.hour.ToString(),
                                                           ti.min.ToString(),
                                                           ti.sec.ToString());
        }
    }



}

TestApplication code:
SecondChangeEvent.Clock myClock = new SecondChangeEvent.Clock();

// Create the display and tell it to
// Observe the clock just created
SecondChangeEvent.DisplayClock dc = new SecondChangeEvent.DisplayClock();
dc.Subscribe(myClock);

// Get the clock started
myClock.Run();


The observer classes do not need to know how the Clock works, and the Clock does not need to know what they are going to do in response to the event. The advantage of the events is that any number of classes can be notified when an event is raised.


Comments

  1. Lighting, DJ and Projector is the main focus of the attraction for any type of events, as well as birthday, anniversary, marriage, etc. Contact us for complete event management services at affordable cost. av rental dallas | audio rental dallas | event lighting rental dallas | dj services provider in dallas | projector screen rental

    ReplyDelete

Post a Comment

Popular posts from this blog

Data Bound Controls in ASP.Net - Part 4 (FormView and DetailsView controls)

JavaScript - ES2015 (aka ES6)

The Clickjacking attack and X-Frame-Options