Friday, November 13, 2009

The WeakEventHandler

When your Observer class hooks an event on an Observable, it implicitly creates a strong reference from Observable to Observer. That means the Observer won't be collected until the Observable is. That means memory leaks at best; latent bugs due to out-of-date observers at worst. So I've written a little WeakEventHandler class that behaves like a weak delegate: it allows the Observer to be collected before the Observable is. Best of all, the syntax is almost identical to the original.

Instead of writing

observable.Event += new EventHandler<MyEventArgs>(this.HandleEvent);

you'd write

observable.Event += new WeakEventHandler<MyEventArgs>(this.HandleEvent);

and the delegate behaves as if it were weak. However there is one downside: the proxy object that is created to pass the event to the observer hangs around. This can cause problems if the event is attached to by a great many short-lived observers. Fortunately there is a solution:

observable.Event += new WeakEventHandler<MyEventArgs>(this.HandleEvent,
handler => observable.Event -= handler);

The implementation of WeakEventHandler is given below.

delegate void EventDetacher<TArgs>(EventHandler<TArgs> handler)
where TArgs : EventArgs;


public class WeakEventHandler <TArgs>
where TArgs : EventArgs
{
MethodInfo method;
WeakReference target;
EventDetacher<TArgs> detacher;


public WeakEventHandler(EventHandler<TArgs> realHandler, EventDetacher<TArgs> detacher)
{
this.method = realHandler.Method;
this.target = new WeakReference(realHandler.Target);
this.detacher = detacher;
}


public WeakEventHandler(EventHandler<TArgs> realHandler)
{
this.method = realHandler.Method;
this.target = new WeakReference(realHandler.Target);
this.detacher = null;
}


private void HandleEvent(object sender, TArgs args)
{
object realObject = target.Target;
if (realObject == null)
{
if (detacher != null)
detacher(this);
}
else
{
method.Invoke(realObject, new object[] { sender, args });
}
}

public static implicit operator EventHandler<TArgs>(WeakEventHandler<TArgs> proxy)
{
return proxy.HandleEvent;
}
}

0 Comments:

Post a Comment

<< Home