IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

Simulating Weak Delegates for EventHandler-like Delegates

Sunday 6 June, 2004, 02:08 PM

[Update: Yes, I know this is broken. There's a bug that means it doesn't work. The complexity in Greg's solution turns out to be necessary... Or at least I've yet to come up with a solution that is simpler and which works.]

Greg Schechter recently wrote a blog on Simulating "Weak Delegates" in the CLR. I thought I'd try a different take on the same theme.

I've written a generic WeakEventHandler class, which provides a delegate with a weak reference to its target. It has an implicit conversion to your chosen delegate type, which means it supports the following usage:

EventHandler h = new WeakEventHandler<EventHandler, EventArgs> (p.Handler);

Obviously the fact that I'm using generics here limits this code to VS.NET 2005. However, the exact same technique I'm using here works just fine without generics if you don't mind writing a version of this class for each delegate type you'd like to support. The example presented here exploits generics so that it will work with any delegate type that uses the EventXxxHandler/EventXxxArgs style.

The nifty thing about this is that the reference the WeakEventHandler holds to its target is a WeakReference. This means that if the WeakEventHandler is the only thing still referring to the target, it won't prevent the target from being garbage collected. For certain styles of event handling (the same scenarios that Greg's code is intended for) this can be useful.

I think this usage model is somewhat simpler - you do not need to alter either the event source or the event handler. You can just connect them using a WeakEventHandler rather than a normal delegate.

Here's the code for the WeakEventHandler:

public class WeakEventHandler<DT, T> where T : EventArgs
{
    private WeakReference weakRefToOriginalDelegate;
    public WeakEventHandler(EventHandler<T> originalDelegate)
    {
        weakRefToOriginalDelegate = new WeakReference(originalDelegate);
    }

    private void DoInvoke(object sender, T args)
    {
        EventHandler<T> originalDelegate = (EventHandler<T>) weakRefToOriginalDelegate.Target;
        if (originalDelegate != null) originalDelegate(sender, args);
    }

    public static implicit operator DT(WeakEventHandler<DT, T> wd)
    {
        object o = Delegate.CreateDelegate(typeof (DT), wd, "DoInvoke");
        return (DT) o;
    }
}

Here's an example showing the delegate in use:

class Program
{
    void Handler(object sender, EventArgs e)
    {
        Console.WriteLine("Handler called");
    }
    static void Main(string[] args)
    {
        Program p = new Program();
        EventHandler h = new WeakEventHandler<EventHandler, EventArgs>(p.Handler);

        h(null, EventArgs.Empty);
        p = null;
        GC.Collect();
        Console.WriteLine("GC done. Raising again:");
        h(null, EventArgs.Empty);
    }
}

The output of this program is:

Handler called
GC done. Raising again:

Note that when we invoke the EventHandler delegate h a second time, the Handler function doesn't run. This is because we forced a garbage collection. The only reference to the Program instance was through the WeakEventHandler, so the Program object got collected, and ceases to receive events.

Generic Constraints and Delegates

I hit a problem when writing this example. I wanted to apply a constraint to the DT type parameter to make sure that a delegate type was always passed in. Unfortunately, when I tried that I got this compiler error:

Cannot convert anonymous block to type 'DT' because it is not a delegate type

This is irritating for two reasons. First, it means that if you try and instantiate the class with a bogus type parameter, e.g.: WeakEventHandler<string, EventArgs>, you won't get a compile-time error. The error will occur at runtime instead, which is unfortunate.

The second reason is that I'm not really happy with that use of Delegate.CreateDelegate. It works, but it shouldn't really be necessary - if the delegateness of the DT type were represented as a constraint, I should be able to do this:

// Doesn't work!

public static implicit operator DT(WeakEventHandler<DT, T> wd)
{
    DT dlg = wd.DoInvoke;
    return dlg;
}

That looks a lot neater to me. That's the code I want to write, and it expresses what the code does. The dynamic creation you have to use in practice is a little less neat, and presumably a little slower since it involves retrieving the function methodinfo by name via reflection. But it's not such a big deal - it's the lack of a compile-time complaint when an inappropriate type parameter is used which annoys me.

The other mildly unsatisfactory thing is that you have to specify both the delegate type and the argument type as type parameters. It feels like you should be able to infer the one from the other. But I can't see a way around this, except for the special case of the generic EventHandler<T> type.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk