Weak Events Without The Fuss With A ‘WeakReference’ Event Proxy

by Dean 28. February 2010 12:42

We all get clobbered at least once by memory leaks in our shiny new applications. One of the main causes of this is when objects that are expected to be garbage collected are not because we have not (or are unable) to unsubscribe them from the event handlers of longer living (static) objects.

The de-facto solution is to implement Microsoft’s ‘WeakEventManager’, but it takes a lot of coding, and adds another layer of complexity to your applications.

Wouldn’t it be great to do something a bit easier like:

public partial class MainWindow : Window
{
    private EventProxy<RoutedEventHandler> proxy = new EventProxy<RoutedEventHandler>();
    public MainWindow()
    {
        InitializeComponent();
        Loaded += WindowLoaded;
    }
 
    private void WindowLoaded(object sender, RoutedEventArgs e)
    {
        AddPublishers();
        var customer = new Customer();
        proxy.Subscribe("ButtonClick", customer.DoCustomerUpdate);
    }
 
    private void AddPublishers()
    {
        updateButton.Click += proxy.AttachPublisher("ButtonClick");
    }
}

 

You could simply subscribe to a number of event publishers on your long living objects (buttons in your main window for example), and then whenever you need to wire-up connections to these events in your short-lived objects, you can simply subscribe to them – as above.

The class that provides all of the ‘glue’ for this solution is called ‘EventProxy’

public class EventProxy<T> where T : class
{
    private readonly List<EventPublisher> subscriptions = new List<EventPublisher>();
 
    public void Subscribe(string sourceEvent, T handler)
    {
        var handlerDelegate = handler as MulticastDelegate;
        if (handlerDelegate == null)
            return;
        var evt = subscriptions.SingleOrDefault(k => k.Name == sourceEvent);
        if (evt == null)
            return;
        evt.TargetHandlers.Add(new EventSubscriber() { Method = handlerDelegate.Method, 
            Instance = new WeakReference(handlerDelegate.Target)});
    }
 
    public T AttachPublisher(string publisherEvent)
    {
        var evt = subscriptions.SingleOrDefault(k => k.Name == publisherEvent);
        if (evt == null)
        {
            evt = new EventPublisher() { Name = publisherEvent };
            evt.Publisher = Delegate.CreateDelegate(typeof (T), evt, EventPublisher.Target, true) as T;
            subscriptions.Add(evt);
        }
        return evt.Publisher;
    }
 
    private class EventPublisher
    {
        public static readonly MethodInfo Target = typeof(EventPublisher).GetMethod(
            "HandleEvent",BindingFlags.Instance | BindingFlags.Public);
        public string Name { get; set; }
        public T Publisher { get; set; }
        public List<EventSubscriber> TargetHandlers { get; private set; }
 
        public EventPublisher()
        {
            TargetHandlers = new List<EventSubscriber>();   
        }
 
        public void HandleEvent(object sender, object e)
        {
            TargetHandlers.RemoveAll(w => w.Instance.Target == null);
            foreach (var reference in TargetHandlers)
            {
                var inst = reference.Instance.Target;
                if (reference.Instance.IsAlive)
                    reference.Method.Invoke(inst, new[] {sender, e});
            }
        }
    }
 
    private class EventSubscriber
    {
        public WeakReference Instance { get; set; }
        public MethodInfo Method { get; set; }
    }
}

 

With this solution, as soon as the short-lived subscriber has gone out of scope, it can be garbage collected as it’s subscription to events is now via a ‘WeakReference’ and my custome ‘EventProxy’ class.

I have done some testing, and it seems to be robust and thread-safe, but Im sure there are room for improvements if you were serious about this type of approach.

 

Dean

Tags: ,

Events

Comments


March 22. 2010 09:50
Dean Chalk
yes

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading

FAO Comment Spammers : Please note that this blog is fully moderated by me personally and no comment spam will ever appear on this site.



RecentComments

Comment RSS
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012 Dean Chalk's Blog