LA.NET [EN]

Nov 23

In the previous two posts, I’ve presented the basics (and some gotchas) associated with the way you declare events. In this post, I’ll present an alternative way for exposing events which is useful when you’re creating a class which has lots of events. In order to understand how this strategy works, we need to make a small detour and see what the compiler does when it finds an event field. Here’s the code we’ve been using for exposing the StudentNameChanged event:

public class Student {
      public event EventHandler<StudentNameChangedEventArgs> StudentNameChanged;
}

Whenever the compiler finds this definition, it ends up generating the following code (ripped with .NET Reflector from the compiled assembly ):

public class Student {
    private EventHandler<StudentNameChangedEventArgs> StudentNameChanged;
    public event EventHandler<StudentNameChangedEventArgs> StudentNameChanged{
        add {
            EventHandler<StudentNameChangedEventArgs> handler;
            EventHandler<StudentNameChangedEventArgs> handler2;
            EventHandler<StudentNameChangedEventArgs> handler3;
            bool flag;
            handler = this.StudentNameChanged;
        Label_0007:
            handler2 = handler;
            handler3 = (EventHandler<StudentNameChangedEventArgs>) Delegate.Combine(handler2, value);
            handler = Interlocked.CompareExchange<EventHandler<StudentNameChangedEventArgs>>(&this.StudentNameChanged, handler3, handler2);
            if (((handler == handler2) == 0) != null) {
                goto Label_0007;
            }
            return;
        }
        remove {
            EventHandler<StudentNameChangedEventArgs> handler;
            EventHandler<StudentNameChangedEventArgs> handler2;
            EventHandler<StudentNameChangedEventArgs> handler3;
            bool flag;
            handler = this.StudentNameChanged;
        Label_0007:
            handler2 = handler;
            handler3 = (EventHandler<StudentNameChangedEventArgs>) Delegate.Remove(handler2, value);
            handler = Interlocked.CompareExchange<EventHandler<StudentNameChangedEventArgs>>(&this.StudentNameChanged, handler3, handler2);
            if (((handler == handler2) == 0) != null) {
                goto Label_0007;
            }
            return;
        }
    }
}

At first sight, the code might look more complex than it really is. As you can see,an event is transformed into a delegate field and the event property ends up generating two methods (add and remove – btw,in the end, don’t forget that you’ll get two methods named add_StudentNameChanged and remove_StudentNameChanged). The add method is used for subscribing an event, while the remove method is called for cancelling a previous subscription. In order to ensure proper working, the code generated for the add and remove methods rely on the CompareExchange method to solve the problems that might arise when our class is used in a multithreaded application (note: the goto label shown can be seen as a do-while loop which keeps adding the passed in delegate until it succeeds in multithreaded environments). Besides that, you’ll surely notice the use of the Combine and Remove static methods used for adding and removing event handlers (I’ll also have a couple of posts about delegates, so I won’t get into this right now).

Now that we know what happens when we define an event, we can see how we can improve our previous event definition by replacing it with an explicit event implementation where the add and remove methods are explicitly defined. Before showing this, it’s important to understand why we need to use a more efficient approach for classes that expose lots of events. The best way to understand why we need this strategy is to think about classes that wrap GUI controls. If you look at the Control class, you’ll notice that it exposes lots and lots of events. If that class exposed its events by using the first approach, we’d end up with lots and lots of fields and that means a lot of memory (for events which might not even be handled by the dev that is using a control).

To solve this memory usage problem, we need to expose events in a more efficient way. To achieve this, we need to add a custom dictionary to our class and then implement our events explicitly through the add and remove methods. Here’s some code that shows how to do this:

public class Student {
    private String _name;
    public String Name {
        get { return _name; }
        set {
            if (value == _name) return;
            var oldName = _name;
            _name = value;
            OnNameChanged( new StudentNameChangedEventArgs(oldName, _name) );
        }
    }
    private static Object _key = new Object(  );
    private Object _locker = new Object(  );
    private EventHandlerList _events = new EventHandlerList(  );
    public event EventHandler<StudentNameChangedEventArgs> StudentNameChanged {
        add {
            lock(_locker) {
                _events.AddHandler( _key, value );
            }
        }
        remove {
            lock(_locker) {
                _events.RemoveHandler( _key, value );
            }
        }
    }
    protected virtual void OnNameChanged(StudentNameChangedEventArgs e) {
        lock(_locker) {
            var handler = (EventHandler<StudentNameChangedEventArgs>)_events[_key];
            if(handler != null ) {
                handler( this, e );
            }
        }
    }
}

As you can see, we’ve added a couple of fields to our class. Besides the EventHandlerList instance, I’ve also added an object used as a key for identifying the StudentNameChanged event in the events custom dictionary (_events field) and another object used for locking purposes (to ensure proper usage of our class in a multithreading environment). Btw, I’ve ended up using the EventHandlerList class since it’s used for all major UI classes introduced by the .NET framework. If you want, you can build your own custom dictionary which takes care of all the goo related with multithreading and invoking the delegates that handle the event (I’ll leave that for you as an exercise).

And I guess this sums it up quite nicely. There might still be a couple of things to say about events, but I think that these last three posts cover most of the important details nicely, so I’ll end up this series here. However, there’s sill a lot of things to talk about .NET and the CLR, so stay tuned for more.

2 comments so far

  1. Tony
    2:14 am - 11-24-2010

    Off topic, what Visual Studio Color scheme are you using?

  2. luisabreu
    8:19 am - 11-24-2010

    I believe it”s the coding horror scheme from http://studiostyl.es/