Sunday, August 17

CLR Weak Event

Since .NET 1.0 Microsoft provide a standard way to register for events of objects.
One of the biggest problem of using the standard event pattern is that the lifetime of the subscriber become governed by the lifetime of the source.
There are several articles about how to overcome this problem, also Microsoft has add a new class to .NET 3.0 called WeakEvent.
I have found WeakEvent class not feat to my needs since it inherit from DependencyObject which is WPF specific object and thus not suited for usage in general program - not related to WPF.
Also, after reading some articles on the net about this issue, I have realized that most of the solution came from the source side, that is changing the object exposing the event to allow weak events, but this is not always the case - sometime you need to you compiled, closed, even seal object and to register to its events weakly.

So, the solution was involved around WeakReference class.
The idea is simple, we want to register to event of source (call it S) with the receiver (call it R).
In classic .NET event S always keep strong reference to R, usually R also keep strong reference to S.
We need to break the strong reference between S and R so that even when S is still alive, it will not keep R alive any longer then it really needs.
We do this by creating new class (call it M) which will mediate the event from the source to the receiver. M will be register to the event on S and will keep a weak reference to R. M will also need to check if R is still alive before dispatching the event.

The code:

class S
{
public event EventHandler SomeHappen;
}

class R
{
R(S s)
{
new M(this, s);
}

private void HandleSomeHappen(object sender, EventArgs e)
{
...
}

private class M
{
private WeakReference _r;
private S _s;

M(R r, S s)
{
_s = s;
_r = new WeakReference(r);
s.SomeHappen += DispatchSomeHappen;
}

private void DispatchSomeHappen(object sender, EventArgs e)
{
R r = (R)_r.Target;
if (r == null) {
_s.SomeHappen -= DispatchSomeHappen;
} else {
r.HandleSomeHappen(sender, e);
}
}
}
}

As you can see this solution is not generic.
I used a private class to dispatch the events sine I want to be able to leave the handle method in the receiver class private or protected.
Most of the work is done in DispatchXXX method which first check if the target has been garbage collected already, in this case we unregister from the event and leave M to be collected next. Otherwise we dispatch the event to the receiver object and let it handle the event.

I'm trying to make this solution more generic, but I yet to find the way.

No comments:

Post a Comment