WeakEventHandler

I came across a problem this week. I was needing a weak delegate to handle a situation where I have a ListBox and I want it to listen to a property changed on a selected ListBoxItem. This situation need to have a weak delegate because if the ListBoxItem is kept referenced, I want to let the ListBox to be garbage collected.

This is how I have implemented the WeakEventHandler. I thought it was a good implementation because it also supports the anonymous methods even if they are closure.

WeakEventHandler

The WeakEventHandler is a thin class inheriting the WeakHandler<T> and provinding a closure delegate in the CreateHandler.

public sealed class WeakEventHandler : WeakHandler<EventHandler>
{
    public WeakEventHandler(EventHandler handler)
        : base(handler)
    {
    }

    protected override EventHandler CreateHandler(WeakReference weakReference)
    {
        return (sender, e) =>
        {
            var h = (EventHandler)weakReference.Target;
            if (h != null)
                h(sender, e);
        };
    }
}

The WeakHandler base class

The WeakHandler is the based class for all delegate type. It provide automatic translation to a weak delegate build by the CreateHandler.

 
public abstract class WeakHandler<T> where T : class
{
    private T handler;
    private T implicitHandler;

    protected WeakHandler(T handler)
    {
        if (handler == null)
            throw new ArgumentNullException("handler");

        if (!(handler is Delegate))
        {
            throw new InvalidOperationException();
        }

        this.handler = handler;
    }

    protected abstract T CreateHandler(WeakReference weakReference);

    public static implicit operator T(WeakHandler<T> weakHandler)
    {
        if (weakHandler == null)
            return null;

        if (weakHandler.implicitHandler == null)
        {
            lock (weakHandler.handler)
            {
                if (weakHandler.implicitHandler == null)
                {
                    var ih = weakHandler.CreateHandler(new WeakReference(weakHandler.handler));
                    Thread.MemoryBarrier();
                    weakHandler.implicitHandler = ih;
                }
            }
        }

        return weakHandler.implicitHandler;
    }
}

 

How to use it

You can use the WeakEventHandler simply as if you where the EventHandler provided by the framework:


Form f = new Form();
f.SizeChanged += new WeakEventHandler(delegate(object sender, EventArgs e)
    {
        Console.WriteLine("Form changed.");
    });

 

Where are we?

We saw how to implement and reuse weak event handler. This event is very easy to create and reuse.

Happy Programming!

2 comments:

Bob Walsh said...
This comment has been removed by the author.
Bob Walsh said...

Interesting, but with this solution, you still keep a strong ref to the WeakEventHandler instance which won't be GC'ed. This is definitely better that keeping a strong ref to the potential bigger object that normally subscribes directly to the event. I guess we still need a real solution for this pattern, but this one is much better than what I've seen so far.