Monday, October 23, 2006

Events and event handlers were the part of the .NET from the beginning. They are used widely throughout the whole BCL (Base Class Library). As a developer I often define my own events using the simple syntax offered by the C# language:

public event EventHandler SomeEvent;

I also prefer to define a protected method that raises the event:

protected void OnSomeEvent()
{
   
if (SomeEvent != null)
   {
      SomeEvent(
this, EventArgs.Empty);
   }
}

Simple as it is, there is one catch to this kind of event declaration. What happens when you declare an event in such a way? "By default, when a class declares an event, the compiler allocates memory for a field to store the event information. If a class has many unused events, they needlessly take up memory" (MSDN). So if your type declares a lot of events then it will consume a lot of memory even if you do not hook up handlers for those events. In order to conserve memory ".NET Framework provides a construct called event properties (custom events in Visual Basic 2005)" (MSDN).

Defining events using the special - explicite - construct is similar to defining a property:

protected EventHandlerList events = new EventHandlerList();
static readonly object eventKey1 = new object();
static readonly object eventKey2 = new object();

public
event EventHandler Event1
{
   
add 
   { 
      events.AddHandler(eventKey1,
value); 
   }
   
remove 
   { 
      events.RemoveHandler(eventKey1,
value); 
   }
}
public event EventHandler Event2
{
   
add
   
{
      events.AddHandler(eventKey2,
value);
   }
   remove
   
{
      events.RemoveHandler(eventKey2,
value);
   }
}

Notice the use of the EventHandlerList class which is a "Provides a simple list of delegates" (MSDN) to store event handlers. Notice also that it is an instance field - one per object instance and the use of static key-objects to use with the EventHandlerList object. This way you end up with only one list field per object to store delegates and a total of N number of object instances where N is the number of events on your type.

Consider the amount of the memory which is saved in this way if you have a collection of thousands of objects of type that defines just a few events!

Unfortunately there is a performance hit when using this alternative syntax because of the additional step needed to retrieve the delagate from the list. Other then this I always declare my events using this syntax if I have more than 2 in one class.

As a side note. This syntax has been avaialble to the C# developers since the 1.1 (or 1.0) version of the .NET Framework. It was not possiblet to do it in VB.NET 2003. According to MSDN: "Event properties are not supported in Visual Basic 2005", but I think this may not be the case since in this same MSDN there is a sample of Visual Basic code:

Private Events As New System.ComponentModel.EventHandlerList

' Define the Click event to use the delegate store.
Public Custom Event Click As EventHandler
   AddHandler(ByVal value As EventHandler)
      Events.AddHandler(
"ClickEvent", value)
   End AddHandler
   RemoveHandler(ByVal value As EventHandler)
      Events.RemoveHandler(
"ClickEvent", value)
   End RemoveHandler
   
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
      
CType(Events("ClickEvent"), EventHandler).Invoke(sender, e)
   
End RaiseEvent
End
Event

Which does exactly the same thing and more! Since events in VB.NET are invoked using the RaiseEvent statement it is also possible to provide an implementation for this. You can use it to raise events asynchronously thanks to it and this pattern is also described in the MSDN.

kick it on DotNetKicks.com