Weak events in C#, take two

Very poorPoorAverageGoodExcellent (No Ratings Yet) 
Loading...Loading...

A few years ago, I blogged about a generic implementation of the weak event pattern in C#. The goal was to mitigate the memory leaks associated with events when you forget to unsubscribe. The implementation was based on the use of weak references to the subscribers, to allow them to be garbage collected.

My initial solution was more a proof of concept than anything else, and had a major performance issue, due to the use of DynamicInvoke every time the event was raised. Over the years, I revisited the weak event problem several times and came up with various solutions, improving a little every time, and I now have an implementation that should be good enough for most use cases. The public API is similar to that of my first solution. Basically, instead of writing an event like this:

public event EventHandler<MyEventArgs> MyEvent;

You write it like this:

private readonly WeakEventSource<MyEventArgs> _myEventSource = new WeakEventSource<MyEventArgs>();
public event EventHandler<MyEventArgs> MyEvent
{
    add { _myEventSource.Subscribe(value); }
    remove { _myEventSource.Unsubscribe(value); }
}

From the subscriber’s point of view, this is no different from a normal event, but the subscriber will be eligible to garbage collection if it’s not referenced anywhere else.

The event publisher can raise the event like this:

_myEventSource.Raise(this, e);

There is a small limitation: the signature of the event has to be EventHandler<TEventArgs> (with any TEventArgs you like, of course). It can’t be something like FooEventHandler, or a custom delegate type. I don’t think this is a major issue, because the vast majority of events in the .NET world follow the recommended pattern void (sender, args), and specific delegate types like FooEventHandler actually have the same signature as EventHandler<FooEventArgs>. I initially tried to make a solution that could work with any delegate signature, but it turned out to be too much of a challenge… for now at least Winking smile.

 

How does it work

This new solution is still based on weak references, but changes the way the target method is called. Rather than using DynamicInvoke, it creates an open-instance delegate for the method when the weak handler is subscribed. What this means is that for an event signature like void EventHandler(object sender, EventArgs e),  it creates a delegate with the signature void OpenEventHandler(object target, object sender, EventArgs e). The extra target parameter represents the instance on which the method is called. To invoke the handler, we just need to get the target from the weak reference, and if it’s still alive,  pass it to the open-instance delegate.

For better performance, this delegate is created only the first time a given handler method is encountered, and is cached for later reuse. This way, if multiple instances of an object subscribe to an event using the same handler method, the delegate is only created the first time, and is reused for subsequent subscribers.

Note that technically, the created delegate is not a “real” open-instance delegate such as those created with Delegate.CreateDelegate. Instead it is created using Linq expressions. The reason is that in a real open-instance delegate, the type of the first parameter must be the type that declares the method, rather than object. Since this information isn’t known statically, I have to dynamically introduce a cast.

 

You can find the source code on GitHub: WeakEvent. A NuGet package is available here: ThomasLevesque.WeakEvent.

The repository also include code snippets for Visual Studio and ReSharper, to make it easier to write the boilerplate code for a weak event.

[C#] A simple implementation of the WeakEvent pattern

Very poorPoorAverageGoodExcellent (2 votes) 
Loading...Loading...

As you probably know, incorrect usage of events is one of the main causes for memory leaks in .NET applications : an event keeps references to its listener objects (through a delegate), which prevents the garbage collector from collecting them when they’re not used anymore. This is especially true of static events, because the references are kept for all the lifetime of the application. If the application often adds handlers to the event and never removes them, the memory usage will grow as long as the application runs, until no more memory is available.

The “obvious” solution, of course, is to unsubscribe from the event when you’re done with it. Unfortunately, it’s not always obvious to know when you can unsubscribe… an object that goes out of scope usually isn’t aware of it, so it doesn’t have a chance to unsubscribe from the event.

Another approach is to implement the WeakEvent pattern, which principle is to keep only weak references to the listeners. That way, unsubscribed listeners can be claimed by the garbage collector. Microsoft included in WPF a few types to deal with the WeakEvent pattern (WeakEventManager class and IWeakEventListener interface), and gives guidelines on how to implement your own weak event. However this technique is not very convenient, because you need to create dedicated classes to expose new events, and the listeners need to implement a specific interface.

So I thought about another implementation, which allows creating weak events almost the same way as normal events. My first idea was to use a list of WeakReferences to store the list of subscribed delegates. But this doesn’t work so well, because of the way we typically use delegates :

myObject.MyEvent += new EventHandler(myObject_MyEvent);

We create a delegate, subscribe it to the event, and… drop it. So the only accessible reference to the delegate is actually a weak reference, so there’s nothing to prevent its garbage collection… and that’s exactly what happens ! After a variable period of time (from my observations, no more than a few seconds), the delegate is garbage collected, and isn’t called anymore when the event is raised.

Rather than keeping a weak reference to the delegate itself, we should use a less transient object : the target object of the delegate (Delegate.Target) would be a better choice. So I created the WeakDelegate<TDelegate> class, which wraps a delegate by storing separately the method and a weak reference to the target :

    public class WeakDelegate<TDelegate> : IEquatable<TDelegate>
    {
        private WeakReference _targetReference;
        private MethodInfo _method;

        public WeakDelegate(Delegate realDelegate)
        {
            if (realDelegate.Target != null)
                _targetReference = new WeakReference(realDelegate.Target);
            else
                _targetReference = null;
            _method = realDelegate.Method;
        }

        public TDelegate GetDelegate()
        {
            return (TDelegate)(object)GetDelegateInternal();
        }

        private Delegate GetDelegateInternal()
        {
            if (_targetReference != null)
            {
                return Delegate.CreateDelegate(typeof(TDelegate), _targetReference.Target, _method);
            }
            else
            {
                return Delegate.CreateDelegate(typeof(TDelegate), _method);
            }
        }

        public bool IsAlive
        {
            get { return _targetReference == null || _targetReference.IsAlive; }
        }


        #region IEquatable<TDelegate> Members

        public bool Equals(TDelegate other)
        {
            Delegate d = (Delegate)(object)other;
            return d != null
                && d.Target == _targetReference.Target
                && d.Method.Equals(_method);
        }

        #endregion

        internal void Invoke(params object[] args)
        {
            Delegate handler = (Delegate)(object)GetDelegateInternal();
            handler.DynamicInvoke(args);
        }
    }

Now, we just need to manage a list of these WeakDelegate<TDelegate>. This is done by the WeakEvent<TDelegate> class :

    public class WeakEvent<TEventHandler>
    {
        private List<WeakDelegate<TEventHandler>> _handlers;

        public WeakEvent()
        {
            _handlers = new List<WeakDelegate<TEventHandler>>();
        }

        public virtual void AddHandler(TEventHandler handler)
        {
            Delegate d = (Delegate)(object)handler;
            _handlers.Add(new WeakDelegate<TEventHandler>(d));
        }

        public virtual void RemoveHandler(TEventHandler handler)
        {
            // also remove "dead" (garbage collected) handlers
            _handlers.RemoveAll(wd => !wd.IsAlive || wd.Equals(handler));
        }

        public virtual void Raise(object sender, EventArgs e)
        {
            var handlers = _handlers.ToArray();
            foreach (var weakDelegate in handlers)
            {
                if (weakDelegate.IsAlive)
                {
                    weakDelegate.Invoke(sender, e);
                }
                else
                {
                    _handlers.Remove(weakDelegate);
                }
            }
        }

        protected List<WeakDelegate<TEventHandler>> Handlers
        {
            get { return _handlers; }
        }
    }

This class automatically handles the removal of “dead” (garbage collected) handlers, and provides a Raise method to call the handlers. It can be used as follows :

        private WeakEvent<EventHandler> _myEvent = new WeakEvent<EventHandler>();
        public event EventHandler MyEvent
        {
            add { _myEvent.AddHandler(value); }
            remove { _myEvent.RemoveHandler(value); }
        }

        protected virtual void OnMyEvent()
        {
            _myEvent.Raise(this, EventArgs.Empty);
        }

This is a bit longer to write than a “regular” event, but considering the benefits, it’s very acceptable. Anyway, you can easily create a Visual Studio snippet to quickly create a weak event, with only 3 fields to fill in :

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>wevt</Title>
      <Shortcut>wevt</Shortcut>
      <Description>Code snippet for a weak event</Description>
      <Author>Thomas Levesque</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>type</ID>
          <ToolTip>Event type</ToolTip>
          <Default>EventHandler</Default>
        </Literal>
        <Literal>
          <ID>event</ID>
          <ToolTip>Event name</ToolTip>
          <Default>MyEvent</Default>
        </Literal>
        <Literal>
          <ID>field</ID>
          <ToolTip>Name of the field holding the registered handlers</ToolTip>
          <Default>_myEvent</Default>
        </Literal>
      </Declarations>
      <Code Language="csharp">
        <![CDATA[private WeakEvent<$type$> $field$ = new WeakEvent<EventHandler>();
        public event $type$ $event$
        {
            add { $field$.AddHandler(value); }
            remove { $field$.RemoveHandler(value); }
        }

        protected virtual void On$event$()
        {
            $field$.Raise(this, EventArgs.Empty);
        }
	$end$]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

This snippet gives the following result in Visual Studio :

Code snippet pour implémenter un WeakEvent

css.php