Event performance optimization with the EventHandlerList

When using custom events (event delegates) in your ASP.NET pages, user controls and server controls, many developers might define their Events as public fields, like this:

publicevent EventHandler MyCustomEvent;

And use this code to fire the event:

protectedvoid OnMyCustomEvent(EventArgs e) { if(MyCustomEvent != null) { MyCustomEvent(this, e); } }

Nothing is wrong with the approach of the latter, but defining the MyCustomEvent as a public field introduces two problems:

    1. Even though no clients have registered any delegates to the invocation list of your event, the compiler generates
      one private delegate field for each event delegate in your class. This is a waste of server memory, especially if
      you have several events inside your class.
  1. One Add and one Remove method is generated for each event field of your class. When clients use += and -= to add
    and remove delegates from the invocation lists of your class’ events, these methods are called behind the scenes.
    These two compiler-generated methods are thread-safe, which means they include extra code to synchronize threads
    that are accessing these methods. This means, that everytime a client adds or removes a delegate to or from the
    invocation list of an event, they have to get a lock before they can do the actual work. This introduces an
    unnecessary overhead because most page developers don’t use multiple threads and therefore there is no need for
    thread synchronization.

The EventHandlerList is the answer to our issues. This class comes with the .NET Framework. The EventHandlerList is a linked list of delegates,which is optimized for adding and removing delegates. To use the EventHandlerList in your classes, you need to add a private static key for each event your class exposes. The code bellow defines a key for MyCustomEvent:

privatestaticreadonlyobject MyCustomEventKey = newobject();

The memory is allocated only once, because the key is static.

After this, you need to define your events as properties – not fields. These event properties has a different syntax that normal get-set properties. Event properties uses add-remove instead of get-set. The following property, is an event property for MyCustomEvent:

publicevent EventHandler MyCustomEvent { add { Events.AddHandler(MyCustomEventKey, value); } remove { Events.RemoveHandler(MyCustomEventKey, value); } }

Every Page, UserControl and WebControl – among others – has a protected property of type EventHandlerList named Events. With this approach, when clients use += to add a delegate to the invocation list of the MyCustomEvent, the add method of the event property calls the AddHandler method of the EventHanderList class, as the code above will tell you. As for -= the RemoveHandler method is called.

The EventHandlerList class maintains a linked list which can have none or one entry for each event. The AddHandler method checks whether this internal linked list contains an entry for an event with the given event key. If it does, the method calls the Combine method of the Delegate class to add the client delegate to the invocation list of the event. If this internal list doesn’t contain an entry for an event with the given event key, the AddHandler method just adds a new entry.

With this new approach, we need to update our OnMyCustomEvent Method, which is responsible for firing the event:

protectedvoid OnMyCustomEvent(EventArgs e) { EventHandler handler = Events[MyCustomEventKey] as EventHandler; if (handler != null) handler(this, e); }

This method uses the MyCustomEventKey as an index in the Events list to access the MyCustomEvent-event. The Events list will return null if it does not contain an entry at the specified index. This will happen when no clients has subscribed to the MyCustomEvent.

Using the EventHandlerList class automatically resolves the two previously mentioned performance problems with the event fields.

Technorati Tags: ASP.NET, Server Controls
kick it on DotNetKicks.com