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
- myClass1.MyEvent += new EventHandler(Function);
Do all stuff
myClass1.MyEvent -= new EventHandler(Function);
- 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
Anonymous
June 27, 2007
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?Anonymous
December 05, 2007
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.Anonymous
March 26, 2008
Event handling subscribe / unsubscribe to an eventAnonymous
July 11, 2008
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, VladAnonymous
February 01, 2013
The comment has been removed