Click here to Skip to main content
15,878,871 members
Articles / Desktop Programming / Windows Forms

An Easy to Use Weak Referenced Event Handler Factory for .NET 2.0

Rate me:
Please Sign up or sign in to vote.
4.84/5 (19 votes)
9 Mar 200710 min read 151.8K   931   87  
An article on creating weak event handlers that show how to use them in a Windows Form.
<!----------------------------- Article Starts ----------------------------->

<UL class=download>
<LI><A href="http://www.codeproject.com/csharp/weakeventhandlerfactory/WeakEventHandlerFactory.zip">Download example project - 25.6 Kb</A> </LI></UL>
<P><IMG height=466 alt="A picture of the sample application running" src="http://www.codeproject.com/csharp/weakeventhandlerfactory/WeakEventHandlerFactory.png" width=599></P>
<H2>Introduction</H2>
<P>While moving from C++/MFC to C#/Windows Forms has been a very positive experience for me, it didn't quite live up to the expectation I had that I could send objects in all directions and never have to worry about deleting them when they are no longer required. The problem is, when you add an event handler to an event, the event will hold a reference to the object that contains the event handler, and so long as that reference is there the garbage collector can't collect the object containing the event handler. Thus while the need to call <CODE>delete</CODE> is gone, excessive use, on my behalf, of events has led me to a point where I need to track when objects are no longer required so that I can remove their event handlers from the event that they are servicing, and thus enable them to be garbage collected.</P>
<P>For a couple of years, I have been using many workarounds for this problem, including changing my coding style to reduce the incidence of this problem, and keeping lists of weak references of delegates that I iterate over to simulate events, but I finally I decided to sit down and write a simple-to-use solution to this problem that would allow me to <B>use events in the way that I want to</B>.</P>
<H2>Background</H2>
<P>I am not the first person to try to solve this problem. Microsoftie Greg Schechter has a <A href="http://blogs.msdn.com/greg_schechter/archive/2004/05/27/143605.aspx" target=_blank>very inspiring article</A> on <A href="http://blogs.msdn.com/greg_schechter/default.aspx" target=_blank>his blog</A> about the problem in general, and the solution that he came up with. While the solution was good, it just didn't quite fill my needs as it required changing my existing codebase too much.</P>
<P>Ian Griffiths of Interact Software Ltd. UK had <A href="http://www.interact-sw.co.uk/iangblog/2004/06/06/weakeventhandler" target=_blank>exactly the sort of solution I was looking for</A> on <A href="http://www.interact-sw.co.uk/iangblog/" target=_blank>his blog</A> but unfortunately it didn't quite work. Tricky problem this one.</P>
<P>Wesner Moise mentions in his blog that he <A href="http://wesnerm.blogs.com/net_undocumented/2005/03/clr_dinner.html" target=_blank>talked about weak delegates</A> with the .NET Bass Class Library team when he had dinner with them back in March 2005, but I haven't seen any mention anywhere of when when such a feature would be implemented so I've decided to go ahead with my own solution. He also mentions on the same page that he has seen two experts screw up an implementation of weak delegates, so here's hoping, with all the possible scrutiny that this article might get, that I haven't added my name to the list of people who have screwed this up.</P>
<H2>Using the Code</H2>
<P>I have two ways of using weak event handlers depending on whether you need speed, or simplicity. The simple way to use weak event handlers is to use the <CODE>WeakEventHandlerFactory</CODE> class which requires that you create <B>one</B> instance of the <CODE>WeakEventHandlerFactory</CODE> in your class:</P><PRE lang=cs>private WeakEventHandlerFactory eventHandlers;</PRE>
<P>And then add as many event handlers as you want, to any event source, using the <CODE>AddWeakHandler</CODE> method:</P><PRE lang=cs>eventHandlers.AddWeakHandler&lt;MyEventArgs&gt;(eventSource, 
              "EventSourceChanged", eventSource_EventSourceChanged);</PRE>
<P>To remove an event handler, use <CODE>RemoveWeakHandler</CODE>:</P><PRE lang=cs>eventHandlers.RemoveWeakHandler&lt;MyEventArgs&gt;(eventSource, 
           "EventSourceChanged", eventSource_EventSourceChanged);</PRE>
<P>Not really too far removed from the code that is normally used to add and remove event handlers, is it? It should be noted that a new feature of the C# 2.0 compiler is that it will convert a function name to an event handler, so <CODE>eventSource_EventSourceChanged</CODE> will be converted to <CODE lang=cs>new EventHandler&lt;MyEventArgs&gt;(eventSource_EventSourceChanged)</CODE>.</P>
<P>When your object is garbage collected, the <CODE>WeakEventHandlerFactory</CODE> will get garbage collected around the same time, and its finalizer will remove all of the event handlers that it has created that haven't been removed yet. It doesn't matter if there is a delay between your object being garbage collected and the event handler factory being garbage collected as the event handler factory will not attempt to pass on events after your object has been garbage collected.</P>
<P>The caveat with using the event handler factory is that it is 40x slower than adding or removing normal event handlers. For some situations this is unacceptable, so I have created a <CODE>WeakEventHandler&lt;&gt;</CODE> class that requires a little more code to use, but in fact ends up being 2x faster to add and remove when compared to normal event handlers. To use Weak Event Handlers directly a little more code and care is required. For each event handler in your class, you need to have an object like the following in your class declaration:</P><PRE lang=cs>private WeakEventHandler&lt;MyEventArgs&gt; eventSourceChangedHandler;</PRE>
<P>Then in your constructor you initialize the <CODE>WeakEventHandler&lt;&gt;</CODE> object in the following way:</P><PRE lang=cs>eventSourceChangedHandler = new 
  WeakEventHandler&lt;MyEventArgs&gt;(eventSource_EventSourceChanged);</PRE>
<P>This is similar to the way you initialize a normal event handler, except that you need to keep an instance of the <CODE>WeakEventHandler&lt;&gt;</CODE> in your class. To use the <CODE>WeakEventHandler&lt;&gt;</CODE> object, simply add it or remove it as you would a normal event handler:</P><PRE lang=cs>eventSource.EventSourceChanged += eventSourceChangedHandler;</PRE><PRE lang=cs>eventSource.EventSourceChanged -= eventSourceChangedHandler;</PRE>
<P>The difference though is that you <B>must</B> remove the event handler before your object has been finalized otherwise your application will bomb with a null reference exception next time the event source fires an event that is handled by a <CODE>WeakEventHandler&lt;&gt;</CODE> in your object. Therefore your finalizer needs to remove the <CODE>WeakEventHandler&lt;&gt;</CODE> like so:</P><PRE lang=cs>~MyClass
{
    eventSource.EventSourceChanged -= eventSourceChangedHandler;
}</PRE>
<H2>Under the Hood</H2>
<P>I've written four utility classes for creating Weak Event Handlers:</P>
<UL>
<LI><CODE>WeakReferenceToEventHandler</CODE> 
<LI><CODE>WeakEventHandlerInternal</CODE> 
<LI><CODE>WeakEventHandlerFactory</CODE> 
<LI><CODE>WeakEventHandler</CODE> </LI></UL>
<H3>WeakReferenceToEventHandler</H3>
<P>This class is exactly what the class name says. It is a weak reference to an event handler. This class is used by the <CODE>WeakEventHandlerInternal</CODE> only, and there should not be any need to use it directly.</P>
<P>This class contains methods to add an event handler to the event source, and remove an event handler from the event source. All the required details such as the event source object and the event name are stored in this class so that it can remove itself from the event source should the original event handler be garbage collected.</P>
<P>As events can't be passed in through methods, the <CODE>AddHandler</CODE> method uses reflection to add an intermediate event handler to the event source:</P><PRE lang=cs>public void AddHandler(object eventSource, string eventName)
{
    // Store the event source details
    
    this.eventName = eventName;
    weakReferenceToEventSource = new WeakReference(eventSource);

    // Create and intermediate handler that the event source will
    // have a strong reference to.
    
    EventInfo eventInfo = eventSource.GetType().GetEvent(eventName);
    eventInfo.AddEventHandler(eventSource, 
      new EventHandler&lt;TEventArgs&gt;(IntermediateEventHandler));
}</PRE>
<P>Similarly, the <CODE>RemoveHandler</CODE> method uses reflection to remove the intermediate event handler:</P><PRE lang=cs>public void RemoveHandler()
{
    if (weakReferenceToEventSource==null)
      return;

    object eventSource = weakReferenceToEventSource.Target;
    if (eventSource != null)
    {
        // Get the event using reflection

        EventInfo eventInfo = eventSource.GetType().GetEvent(eventName);

        // Remove the intermediate event handler, which will dereference
        // this weak reference and allow it to be garbage collected.

        eventInfo.RemoveEventHandler(eventSource, 
          new EventHandler&lt;TEventArgs&gt;(IntermediateEventHandler));
    }
}</PRE>
<P>When the event gets fired, the intermediate event handler handles it. It's at this point that a test is done to see if the original event handler has been garbage collected, and if so then the intermediate event handler removes itself from the event source.</P>
<H3>WeakEventHandlerInternal</H3>
<P>The <CODE>WeakEventHandlerInternal</CODE> class is used internally for storing weak event handlers. This class should not be used directly, so I made the constructor <CODE lang=cs>internal</CODE>. I have a class called <CODE>WeakEventHandler</CODE> to be used directly that is 8x faster than <CODE>WeakEventHandlerInternal</CODE> for adding and removing events. The <CODE>WeakEventHandlerInternal</CODE> class is a container for the strong reference to the original event handler, and also contains an instance of the <CODE>WeakReferenceToEventHandler</CODE> class. It contains methods for adding, removing, and comparing weak event handlers for equality. It is used by the <CODE>WeakEventHandlerFactory</CODE> class.</P>
<H3>WeakEventHandlerFactory</H3>
<P>The <CODE>WeakEventHandlerFactory</CODE> is the class that can be used to create weak event handlers. It contains two methods:</P>
<UL class=method>
<LI><CODE>AddWeakHandler</CODE> 
<LI><CODE>RemoveWeakHandler</CODE> </LI></UL>
<P>The <CODE>AddWeakHandler</CODE> method creates a weak reference to the original event handler, but also stores a strong reference to the event handler so that it isn't garbage collected as soon as we leave the method. The weak reference and strong reference are stored together in an object that is added to an internal list.</P>
<P>The <CODE>RemoveWeakHandler</CODE> does a search through all the stored event handlers looking for a match, and removes the event handler if it finds a match. This method returns a flag that indicates if it was successful or not.</P>
<H3>WeakEventHandler</H3>
<P>This class can be used directly, but cautiously, for directly creating weak event handlers. It sacrifices ease of use for speed. An instance of <CODE>WeakEventHandler</CODE> must be held for each event handler that is represented by a <CODE>WeakEventHandler</CODE> object, and each <CODE>WeakEventHandler</CODE> object must be removed from the source event before the event handling object is garbage collected.</P>
<H2>Using the Sample Application</H2>
<P>If you compile the project linked in at the top of this article, you will get an application that looks like the image at the top. This application demonstrates the usefulness of a weak event handler.</P>
<P>The application has three <CODE>DataGrid</CODE> controls that are used for editing the contents of up to three collections. Above each <CODE>DataGrid</CODE> is a unique identifier for the collection being edited in that <CODE>DataGrid</CODE>, as well as the total of all the values held in that collection.</P>
<P>If you edit one of the collection items, the total displayed above the <CODE>DataGrid</CODE> will be updated. If you remove one of the items, you will see a message in the log window indicating that the event handler was successfully removed from the item that you removed from the collection. From this you can deduce that each item in the collection has an event that fires when you change the item. Thus each item has a reference to its parent collection(s) as it is the collection that handles the event and updates the value of the total held in the collection object. The purpose of this application is to show that a weak reference to the event handler is used allowing a container of objects (the collection) to be garbage collected if it is no longer required, even if the items in the collection still exist.</P>
<P>Now for the fun part. Below each <CODE>DataGrid</CODE> are four buttons. They allow you to display, or copy one of the other two collections.</P>
<P>When you copy a collection, a new collection is created and the objects from the source collection are copied into it. Hitting the "Force Garbage Collection" button will force the replaced collection to be garbage collected if it is no longer being displayed anywhere, and the log window will show a message indicating which collection was garbage collected.</P>
<P>Editing an item in a copied collection will update the collection it was copied from, but items added to or removed from the new collection will have no effect on the source collection.</P>
<P>When you "View a Set", the actual collection itself will then be displayed in the destination <CODE>DataGrid</CODE>, as well as the source <CODE>DataGrid</CODE>. Actions of adding/deleting/editing objects in one <CODE>DataGrid</CODE> will be mirrored in the other <CODE>DataGrid</CODE>.</P>
<H2>Speed Testing</H2>
<P>The sample application includes a little benchmark to show how fast different event handler types can be added and removed. I tested adding and removing 1,000,000 event handlers on an old 3.0GHz Pentium 4 and got the following results:</P><PRE lang=text>Speed Test: Adding and Removing 1000000 Event Handlers

Adding Normally.........Seconds: 0.5781361
Removing Normally.........Seconds: 0.3906325
Adding WeakEventHandler.........Seconds: 0.156253
Removing WeakEventHandler.........Seconds: 0.2343795
Adding WeakEventHandlerInternal.........Seconds: 2.2812938
Removing WeakEventHandlerInternal.........Seconds: 1.9687878
Adding using WeakEventHandlerFactory.........Seconds: 19.7191286
Removing using WeakEventHandlerFactory.........Seconds: 17.1253288</PRE>
<P>In the sample application I have set the number of event handlers to add and remove to 100,000 so that you don't have to wait too long to see your own results.</P>
<H2>Sample Code Included in the Sample Application</H2>
<P>There are two collection classes included in the source code for the sample application:</P>
<UL>
<LI><CODE>SubTotalCollection</CODE> 
<LI><CODE>SubTotalCollectionFast</CODE> </LI></UL>
<P><CODE>SubTotalCollection</CODE> uses the <CODE>EventHandlerFactory</CODE> to create weak event handlers, and <CODE>SubTotalCollectionFast</CODE> uses the <CODE>WeakEventHandler</CODE> class directly. <CODE>SubTotalCollectionFast</CODE> is a good example of managing <CODE>WeakEventHandler</CODE> objects. It adds a handler when a <CODE>SubTotal</CODE> object is added to the collection, it removes a handler when a <CODE>SubTotal</CODE> object is removed from the collection, and when it is finalized or cleared it removes event handlers from all the <CODE>SubTotal</CODE> objects still in the collection.</P>
<P>The sample application just uses the <CODE>SubTotalCollection</CODE> class. To make it use the <CODE>SubTotalCollectionFast</CODE> class, just do a search and replace in the <I>Form1.cs</I> file replacing <CODE>SubTotalCollection</CODE> with <CODE>SubTotalCollectionFast</CODE>.</P>
<H2>Points of Interest</H2>
<P>The new <CODE>DataGrid</CODE> control is a lot easier to use than the old one, for editing simple collections. In this sample application, I have objects of type <CODE>SubTotal</CODE> in a collection that I want to edit in a <CODE>DataGrid</CODE>. There were three steps to doing this. The first was declaring a collection class that inherited from <CODE>BindingList</CODE>, like so:</P><PRE lang=cs>public class SubTotalCollection : BindingList&lt;SubTotal&gt;</PRE>
<P>Then in <I>Form.cs</I> I created a binding source, attached it to the <CODE>DataGrid</CODE>, and attached a collection to the binding source, and that was it:</P><PRE lang=cs>BindingSource leftSource = new BindingSource();
dataGridViewLeft.DataSource = leftSource;
leftSource.Source = new SubTotalCollection();</PRE>
<P>Between this version and the first version posted on September 23<SUP>rd</SUP>, 2005, I had many attempts at making the factory faster. After putting in a <CODE>Dictionary</CODE> for finding matching event handlers in the <CODE>RemoveHandler</CODE> method I tried changing it to <CODE>SortedDictionary&lt;,&gt;</CODE>, however for this purpose the <CODE>SortedDictionary&lt;,&gt;</CODE> seemed to take twice as long as using the <CODE>Dictionary&lt;,&gt;</CODE> class.</P>
<H2>History</H2>
<UL>
<LI>Original article submitted on September 23<SUP>rd</SUP>, 2005. 
<LI>October 13<SUP>th</SUP> 2005 - improved speed, added more comments, added faster <CODE>WeakEventHandler</CODE> class. <LI>March 7<SUP>th</SUP> 2007 - fixed a bug in the faster <CODE>WeakEventHandler</CODE> class found by St�phane Issartel.
</LI></UL>



<!----------------------------- Article Ends ----------------------------->

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Founder Cheesy Design
Taiwan Taiwan
John graduated from the University of South Australia in 1997 with a Bachelor of Electronic Engineering Degree, and since then he has worked on hardware and software in many fields including Aerospace, Defence, and Medical giving him over 10 of years experience in C++ and C# programming. In 2009 John Started his own contracting company doing business between Taiwan and Australia.

Comments and Discussions