Thursday 16 May 2013

C# Delegate VS Event


Both Delegate instance and a delegate instance with event modifier are used to implement event publisher-subscriber design pattern. Without going into definitions of delegates and events, I will try to explain the difference between delegates and events (event keyword/modifier). In either cases, events are implemented using delegates.

Quoting from MSDN: "The publishing class defines a delegate. The subscribing class does two things: first, it creates a method that matches the signature of the delegate, and then it creates an instance of that delegate type encapsulating that method. When the event is raised, the subscribing class's methods are invoked through the delegate".



1) Delegate is a type whereas an event is only a modifier. This means a delegate can be declared outside of a class but an event cannot.

2) Although both delegate instance and a delegate instance with event modifier can be used to raise an event (to separate the events I have called the event generated by delegate as delegate event and event generated by delegate with event modifier as event event), the delegate event can be raised from other classes too and not just the class which contains the delegate event. Whereas an event event can only be raised by the class which contains the event.

3) Comparing the delegate declaration with an instance of delegate with event modifier (and not comparing the delegate instance with the instance of delegate with event modifier), obviously we need to first create a delegate instance to register the subscriber method with the delegate event and cannot register the subscriber method using only the delegate type (IntimateAttendees).

4) An event handler (method subscribed with the event), can be registered with the delegate event using either assignment operator (=) or subscribe operator (+=) whereas an event event (event using event modifier) uses only subscribe operator to register an event handler with the event.

5) A delegate instance cannot be declared in an interface (which is a field) whereas a delegate instance marked as event can. The compiler does not consider it a field.

I could not get a link to attach the code so, although the code is produced below, if you need the code files then please let me know and I shall send the code. Though the events are raised just like normal method calls, but my focus is to show the differences between delegate and event without engaging in too much code. Goes without saying, it can be refined to trigger the events as events and not as pure method calls.
_________________________________________________________________________________
Code:



using System;

namespace EventsAndDelegates
{
    #region Difference 1
    // Difference 1
    public delegate void OnlyATest(bool CallStarted);
    // The following code shows that delegate is a type where event is a only a modifier and not a type.
    // public event OnlyATestDelegate OnlyATestDelegateEvent;   // This will result in compilation error as no type is defined.
    #endregion Difference 1

    class ConferenceCall
    {
        public delegate void IntimateAttendeesHandler(bool CallStarted);
        public event IntimateAttendeesHandler CallStartedEventRaisesEvent;  // Declare event in Publisher - Event Event
        public IntimateAttendeesHandler CallStartedDelegateRaisesEvent;     // Declare event in Publisher - Delegate Event

        // Raising the event
        public void Run()
        {
            #region Conventional EventHandler using EventArgs base class
            // ConferenceCallEventArgs conferenceCallEventArgs = new ConferenceCallEventArgs(DateTime.Now, 2);
            // Conventially event handlers accept 2 parameters; source object and the specific information about the event (object derived from EventArgs)
            // The following call is useless as neither of the arguments are send but it is done to prove that the arguments can be different.
            // The ConferenceCallEventArgs can be used to refine this code so that it works in the conventional way.
            #endregion Conventional EventHandler using EventArgs base class

            CallStartedDelegateRaisesEvent(true);
            CallStartedEventRaisesEvent(true);

            #region Difference 2
            /*
             * The problem with raising the event using delegates is that it can be raised by other classes too and not just by the class
             * which publishes the event. In this case CallStartedDelegateRaisesEvent(true) can be called from the 'Test' class
             * defined subsequently in the code.
             * Whereas the event (same delegate type but using an event keyword) can only be raised by the class
             * which defines the event. In this case if we try to call CallStartedEventRaisesEvent(true) from the 'Test' class,
             * compiler will throw an error.
             * */
            #endregion Difference 2
        }
    }

    class Attendee1
    {

        #region Difference 3
        public void Difference2()
        {
            /*
             * The difference is not between the delegate and event instances. Rather comparing
             * delegate declaration with event instance. So not exactly a difference but worth noting.
             * Obviously we need to create a delegate instance to register the subscriber method with the delegate event.
             * And cannot add the subscriber method using only the delegate type (IntimateAttendees)
             * */
            ConferenceCall conferenceCall = new ConferenceCall();
            ConferenceCall.IntimateAttendeesHandler delegateInstance = new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);
            // The following will throw an error as we cannot use assignment operator while subscribing to an event
            // in case of event. In case of delegate its perfectly valid. This is Difference 4 which is shown later.
            // conferenceCall.CallStartedEventRaisesEvent = new ConferenceCall.IntimateAttendees(SubscribedMethod);
            conferenceCall.CallStartedEventRaisesEvent += new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);

            conferenceCall.CallStartedEventRaisesEvent -= new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);    // Unsubscribe the method.
        }
        #endregion Difference 3

        // Subscribing to event raised by delegate
        public void SubscribeToDelegateEvent(ConferenceCall conferenceCall)
        {

            conferenceCall.CallStartedDelegateRaisesEvent += new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);

        }

        // Subscribing to event raised by event
        public void SubscribeToEventEvent(ConferenceCall conferenceCall)
        {

            conferenceCall.CallStartedEventRaisesEvent += new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);

        }

        // Callback Method
        public void SubscribedMethod(bool CallStarted)
        {
            Console.WriteLine("Attendee1 is online");
        }


    }

    class Attendee2
    {
        // Subscribing to event raised by delegate
        public void SubscribeToDelegateEvent(ConferenceCall conferenceCall)
        {

            conferenceCall.CallStartedDelegateRaisesEvent += new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);
            #region Difference 4
            /*
             * The following code will also work (note that only assignment operator is used);
             * conferenceCall.CallStartedDelegateRaisesEvent = new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);
             * This will remove the methods already registerd with the CallStartedDelegateRaisesEvent event.
             * But if we replace the code in following method with the following line;
             * conferenceCall.CallStartedEventRaisesEvent = new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);
             * the compiler will not like it. The reason is the delegate instance CallStartedEventRaisesEvent is an event
             * (marked by event keyword) and hence the registration of the subscriber method (event handlers) with the event
             * is only possible through subscribe operator.
             * */
            #endregion Difference 4

        }

        // Subscribing to event raised by event
        public void SubscribeToEventEvent(ConferenceCall conferenceCall)
        {

            conferenceCall.CallStartedEventRaisesEvent += new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);

        }

        // Callback Method
        public void SubscribedMethod(bool CallStarted)
        {
            Console.WriteLine("Attendee2 is online");
        }
    }

    class Test
    {

        ConferenceCall conferenceCall = new ConferenceCall();
        Attendee1 attendee1 = new Attendee1();
        Attendee2 attendee2 = new Attendee2();

        public void Run()
        {
            attendee1.SubscribeToDelegateEvent(conferenceCall);
            attendee1.SubscribeToEventEvent(conferenceCall);

            attendee2.SubscribeToDelegateEvent(conferenceCall);
            attendee2.SubscribeToEventEvent(conferenceCall);

            conferenceCall.Run();
        }
     
    }

    interface ITest
    {
        #region Difference 5
        // A delegate instance cannot be declared in an interface (which is a field)
        // whereas an event instance can. The compiler does not consider it a field.

        // OnlyATest DelegateInstance       //Error
        #endregion Difference 5
        event OnlyATest EventInstance;
    }
 
}