Simplifying the WeakEvent Pattern

Building on the work started by William Kempf, I set out to make using the WeakEvent Pattern simpler and more readable. My intention was to make sure the user would have to write the minimum amount of code to use it, in order to draw it closer to the more intuitive model of strongly referenced events. To use, download the project, reference it and go over the following steps:

Using the Model

  1. Create an EventManager. This class will be used by the mechanism internally to register to the object's strongly referenced event itself. Note that IMyEvent is simply an interface containing the event's description and can therefore be replaced with the type exposing the event itself, rather than a special interface. Also note that the EventManager inherits from WeakEventManagerBase with itself and the type it uses as generic parameters.
    public sealed class MyEventManager
    : WeakEventManagerBase<MyEventManager, IMyEvent>
    {
    protected override void StartListeningTo(IMyEvent source)
    {
    source.MyEvent += DeliverEvent;
    }

    protected override void StopListeningTo(IMyEvent source)
    {
    source.MyEvent -= DeliverEvent;
    }
    }
  2. Add a WeakHandler field to the type containing the method and reference that same method, much like a simple delegate instance. This is done to keep the WeakHandler's reference alive for the duration the containing type lives.
    private readonly WeakHandler<MyEventManager, MyEventArgs> handler;

    ...

    this
    .handler = new WeakHandler<MyEventManager, MyEventArgs>(this.listener_MyEvent);
  3. Register to the object's event. Note that all types are inferred, except for the type specified as the provider of the event.
    handler.ListenTo(sender as IMyEvent);
  4. Raising the event will now call the referenced method for as long as the type containing the handler lives.

Points of Interest Behind the Scenes

  1. The WeakHandler's ListenTo and StopListeningTo methods are extension methods so that you wouldn't have to specify TSource too when creating the WeakHandler (in the example above - IMyEvent).
  2. The WeakHandler's constructor receives EventHandler<TArgs> so that you could simply specify a method and it would implicitly be converted to the delegate type, instead of having to use the whole delegate syntax.
  3. A WeakHandler can be assigned 0..n methods, using its Append and Remove methods. The intuitive comparison to null (or other WeakHandlers) is also available to determine if there are methods referenced by the instance, but calls to methods on the instance will not raise a NullReferenceException.
  4. All methods placed in the WeakHandler's list must be instance methods (there is no reason to use a WeakHandler with static methods) that were declared in the type the WeakHandler was declared on. This is to prevent a situation where object A declares a WeakHandler, but references a method on object B, causing object B to live at least until object A is dereferenced, dereferencing the WeakHandler field.

[Update: Within minutes of posting this, the post has been linked to from the Microsoft Israel homepage. I am truly honored.]

No Comments