subscribe / unsubscribe to an event


In one of the internal aliases there was a discussion going on some time back regarding which is the better way to unsubscribe from an event and why does the first one even work


 



  1. myClass1.MyEvent += new EventHandler(Function);
    Do all stuff
    myClass1.MyEvent -= new EventHandler(Function);

 



  1. EventHandler eventHandler = new EventHandler(Function);
    myClass2.MyEvent += eventHandler;
    myClass2.OnEvent();
    myClass2.MyEvent -= eventHandler;

 


In the first approach different objects are used to add and remove the event subscription. This can work only if the removal mechanism does not work by just comparing references. Because if it did then the removal mechanism would not find the object in the methods invocation list of the underlying delegate and would simply not remove the method.


 


I thought I’d do a little more snooping into this.


 


A class definition like
 public delegate void EventHandler(Object source, EventArgs eventArgs);


    class MyClass


    {


        public event EventHandler MyEvent;


}


Is converted to code equivalent to


 


    class MyClass


    {


        private EventHandler __MyEvent;


        public event EventHandler MyEvent


        {


            add


            {


                lock (this) { __MyEvent += value; }


            }


            remove


            {


                lock (this) { __MyEvent -= value; }


            }


        }


}


So the compiler generates the add/remove accessors.


 


If you see the code of the accessors in ILDasm then you can see for the add_MyEvent method compiler emits code to call the System.Delegate.Combine method to combine the existing delegate and new delegate and for remove_MyEvent it uses System.Delegate.Remove method.


 


So


 


myClass1.MyEvent += new EventHandler(Function); è System.Delegate.Combine (myClass1.__MyEvent, newdelegate)


 


and


myClass1.MyEvent -= new EventHandler(Function); è System.Delegate.Remove (myClass1, newdelegate)


 


So all of the trick lies in the Combine and Remove methods of System.Delegate.


 


System.Delegate.Remove goes through the invocation list of the first delegate looking for invocations from the second delegate. If there is a match it removes it. System.Delegate overrides the == (Equals) operator so that the match does not happen based on object reference but based on the method the delegate encapsulates. This operator compares the instance object (null in case of static method callback) and the function pointed to and then removes the callback from the invocation list.


 


So both works but which one should we use? If the events are subscribed/unsubscribed once at the beginning/end like in a typical WinForm application then it hardly matters. However if this is done multiple times then the second approach is preferable as it does less of costly heap allocations and will work faster.


 


The complete code listing of the sample I used is as follows


 


using System;


using System.Collections.Generic;


using System.Text;


 


namespace EventAddRemove


{


    public delegate void EventHandler(Object source, EventArgs eventArgs);


 


    class MyClass


    {


        public event EventHandler MyEvent;



        public void OnEvent()


        {


            if (MyEvent != null)


                MyEvent(this, EventArgs.Empty);


            else


                Console.WriteLine(“No Event to fire”);


        }


    }


   


    class Program


    {


        static void Function(object source, EventArgs eventArgs)


        {


            Console.WriteLine(“Event got fired”);


        }


 


        static void Main(string[] args)


        {


            Console.WriteLine(“myClass1”);


            MyClass myClass1 = new MyClass();


            myClass1.MyEvent += new EventHandler(Function);


            myClass1.OnEvent();


 


            myClass1.MyEvent -= new EventHandler(Function);


            myClass1.OnEvent();


 


 


            Console.WriteLine(“myClass2”);


            EventHandler eventHandler = new EventHandler(Function);


 


            MyClass myClass2 = new MyClass();


            myClass2.MyEvent += eventHandler;


            myClass2.OnEvent();


 


            myClass2.MyEvent -= eventHandler;


            myClass2.OnEvent();


        }


    }


}


 


Output is


myClass1


Event got fired


No Event to fire


myClass2


Event got fired


No Event to fire


Lines in bold indicate that the correct methods was indeed removed from the invocation list in both approaches.

Comments (5)

  1. anon says:

    nice work! question tho …

    "If you see the code of the accessors in ILDasm then you can see for the add_MyEvent method compiler emits code to call the System.Delegate.Combine method to combine the existing delegate and new delegate and for remove_MyEvent it uses System.Delegate.Remove method."

    how exactly did you do/determine this?

  2. Matt Thalman says:

    I haven’t seen much perf difference between creating two event handlers or just one if you are using named methods.  However, if your delegate is an anonymous method, I’ve seen the results be 12x slower if you don’t assign it to a variable.

  3. Event handling subscribe / unsubscribe to an event

  4. Vlad says:

    I have a situation where I need to do things a little backwards.  

    I register for an event.

    I then unregister to that event and do something and reregister somewhere else.

    So, I would like to test that the event was registered before I unregister it.

    How can I do (pseudo code I wish would work)

    if (MyClass.MyEvent.Count > 0)

     MyClass.MyEvent -= eventHandler;

    Thanks,

    Vlad

  5. S says:

    @Vlad test it and see, but I'm pretty sure nothing will blow up if you try to unregistered an event that is not registered.