Click here to Skip to main content
15,884,473 members
Articles / Programming Languages / C# 4.0
Article

Simple effective Weak Event Dispatcher in C#

Rate me:
Please Sign up or sign in to vote.
4.00/5 (5 votes)
7 Apr 2011CPOL3 min read 33.8K   468   23   7
Another weak event dispatcher in C#

Introduction

Some time ago, I needed to register quite a lot of event handlers. At that point, I got aware of the memory leaking if you do not unregister unneeded event handlers. The reason is that such a handler's instance keeps being referenced by the corresponding event dispatcher, preventing the garbage collector from garbage collecting that corresponding instance.

Another related problem was that unneeded handlers kept being executed since their instances could not be garbage collected, resulting in loss of performance. So unregistering events is an important thing to do if you have the opportunity to do that.

But of course, it would be comfortable not to worry about unregistering events at all.

I discovered this to be a fairly common issue and I found many solutions and some nice articles on the web referring to the WeakReference and WeakEventManager classes. These solutions partly worked for me as they dealt with the memory leakage. But for each, I encountered performance or usability issues, and I decided to write a simple weak event dispatcher with focus on usability and performance.

Background

A weak reference is a reference which does not count for the garbage collector when it needs to decide about garbage collecting the corresponding instance. This implies that a weak reference can be null at a sudden moment because it was garbage collected as there were no other strong references anymore to that particular instance. This also means that you need to take care when using weak references.

Using the code

This WeakEventDispatcher acts like a "normal" event dispatcher on the outside, i.e., you can add and remove handlers easily. On the inside, handlers are bucketed together with weak references to their instances. The invocation is done by compiled lambda expressions, i.e., delegates. These delegates are cached and reused where possible. The purging (getting rid of handlers belonging to garbage collected instances) is done on a regular basis which can be configured by a threshold setting. As a result, there is no memory leakage anymore, and the event invocation performance increases significantly if you have to deal with a lot of event handling.

A unit test class is included which shows how to deal with the WeakEventDispatcher. Further, a test method is included for performance measurement. It shows the performance differences between applying standard .NETevent handling and applying the weak event dispatcher.

The performance test tells that you gain more performance benefit as you increase the number of event handling iterations. If you largely increase the number of iterations, the standard .NET event handling eventually entangles by the number of handler invocations since "lost" instances are not garbage collected and as a result the corresponding handlers keep being invoked. The WeakEventDispatcher prevents that from happening.

The following example shows how to apply the WeakEventDispatcher:

C#
public class Entity {
    private readonly WeakEventDispatcher<EventArgs> _changeNotificationDispatcher;

    public event EventHandler<EventArgs> DataChanged {
        add { _changeNotificationDispatcher += value; }
        remove { _changeNotificationDispatcher -= value; }
    }
 
    protected virtual void OnDataChanged(EventArgs e) {
        if(_changeNotificationDispatcher!= null)
            _changeNotificationDispatcher.Invoke(this, e);
    }
}

Inside the event dispatcher, some housekeeping needs to be done once in a while, i.e., the handlers which belonged to garbage collected instances need to be removed. The housekeeping is performed by Purge(). The purging can be performed manually, but is done regularly as well every configurable number of times that a public dispatcher method is called. The interval is configured by the PurgeThreshold, and the corresponding value can be passed with the dispatcher's constructor. You could instantiate the dispatcher at declaration and pass a purge threshold setting, like:

C#
private readonly WeakEventDispatcher<EventArgs> 
  _changeNotificationDispatcher = new WeakEventDispatcher<EventArgs>(10);

Now you set the threshold to 10, meaning that a purge is performed every ten Invoke(...) or += calls. So with this value, you define how frequently Purge() is performed. If you pass 0, then Purge() is performed each time you touch any public dispatcher method. By default, the purge threshold is set to 5.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Netherlands Netherlands
Jaap Lamfers works as an independent .Net application architect and developer.

Comments and Discussions

 
QuestionException thrown if a handler is defined as extension Pin
pcdonp4-May-15 1:34
pcdonp4-May-15 1:34 
AnswerResolved :) Pin
pcdonp4-May-15 4:28
pcdonp4-May-15 4:28 
QuestionQuestion about your example of usage. Pin
Elmar Debets4-Sep-14 7:06
Elmar Debets4-Sep-14 7:06 
AnswerRe: Question about your example of usage. Pin
pcdonp4-May-15 3:32
pcdonp4-May-15 3:32 
GeneralMy vote of 3 Pin
hemantwithu3-Apr-12 2:29
hemantwithu3-Apr-12 2:29 
Generalnice but you need to explain how it works as well as how to use it Pin
Sacha Barber8-Apr-11 2:43
Sacha Barber8-Apr-11 2:43 
GeneralRe: nice but you need to explain how it works as well as how to use it Pin
Jaap Lamfers18-Apr-11 5:09
Jaap Lamfers18-Apr-11 5:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.