Weak events in C#, take two

Very poorPoorAverageGoodExcellent (1 votes) 

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.


  1. Joseph N. Musser II says:

    I enjoy your articles. The line about a solution that would work for any delegate type being too much of a challenge got the wheels turning…

    API-wise, you could expose Raise as a delegate instance instead of a hardcoded method. That way generics gives you parameters types and names and even return type for free!

    I put something together that you might enjoy:

    I think it’s a lot less complex. I wanted to keep the original delegate, target and all, so that I could duplicate the unsubscribe behavior people are used to- remove delegate by reference, not by target reference and method equality. I didn’t end up having to manage the delegate target at all, so I didn’t need to introduce WeakDelegate or OpenEventHandler.

    Also, one very important issue for me is to make sure I never invoke a delegate while holding a lock. It raises lock contention and it often introduces subtle reentrancy bugs in random parts of the host program that drive me insane trying to track down.

    Thanks for the great article, keep it up!

    • Thomas Levesque says:

      Hi Joseph,

      Thanks for your comment. I’m on vacation for a few days with no access to my dev PC, so I can’t really look at your idea right now. But it looks interesting, I’ll look into it as soon as I’m back home 😉

    • Thomas Levesque says:

      Hi Joseph,

      I just had a look at your solution. Actually, keeping a weak reference to the delegate itself is something I tried before, but it has a serious drawback: the subscriber needs to keep a strong reference to the delegate, otherwise the delegate is garbage collected too early!

      Here’s a snippet that demonstrates the issue:

      As you can see, the Subscriber.OnFoo method is never invoked, because the delegate has already been collected.

      • Joseph N. Musser II says:

        That is an excellent point. I was mistaken- delegates are removed by value, not reference. So my code oversimplifies. I updated my code (see original link) to resolve this issue using a ConditionalWeakTable to guarantee that as long as the target is alive, the delegate will stay alive. If the target does not stay alive, the delegate gets collected.

      • Joseph N. Musser II says:

        And here is a version that introduces WeakDelegate and does not need ConditionalWeakTable: https://gist.github.com/jnm2/d78cd49335615418411b

  2. Francois Botha says:


    How does this approach compare to just using WeakEventManager , i.e. the approach outlined in http://www.codeproject.com/Articles/738109/The-NET-weak-event-pattern-in-Csharp ?

    • Thomas Levesque says:

      Well, IMO WeakEventManager isn’t very natural to use; there’s a lot of boilerplate code to write, and it doesn’t look anything like a normal event. Other than that, I don’t think there are fundamental differences between the two.

      • Francois Botha says:

        The .NET 4.5 WeakEventManager class is a generic, so removes a lot of the boilerplate which I believe you’re referring to. But, yes, I agree that your implementation looks more like a normal event. I prefer this pattern too.

  3. Ben Ernst says:

    This is great, it’s a nice clean readable implementation, with tests, and it works well.

2 Trackbacks

Leave a comment