Weak events in C#, take two

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.

19 thoughts on “Weak events in C#, take two”

  1. 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!

    1. 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 😉

    2. 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.

      1. 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.

    1. 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.

      1. 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.

        1. I dont’t like this pattern as users are hard to know whether the event is strong type or weak type. I prefer all event sources are strong. When subscriber needs weak, use weak event manager to subscribe it as weak. The pattern is more clear to know what is strong and weak.

  2. Hi,
    Your weakevent is threadsafe ?
    I create an application for high frequency messaging and I want to use an observable pattern.
    I search the best methode between event and interface implementation.
    I need no memory leaks, fast execution and threadsafe.
    Its possible to add an execution in the publisher when a new subscribe come (ex: send cahe).


    1. Hi Sylvain,

      Yes, it should be thread-safe; there’s a lock around every access the the list of handlers, and the WeakDelegate themselves are immutable. However, looking at the code again, I realize that it probably locks too much… Currently the handlers are invoked while the lock is taken when raising the event, which means subscribing/unsubscribing is blocked during execution of the handlers. It should probably be changed.

      1. Thanks,

        If i want do something when a subsribe/unscribe is calling, do you see a solution ?

        In the pattern “observer” I call a function on the observer he subscribes.

        1. Hi Sylvain,

          Since the event’s add/remove accessors are defined manually, you could add some logic in those accessors.

  3. You might want to have a look at Solving the Problem with Events: Weak Event Handlers (in the internet archive at https://web.archive.org/web/20150405151753/http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx#commentstart) That approach is similar but uses a real open delegate, hiding the extra type parameter behind an interface. It also provides an exposed Unsubscribe callback so that you can choose to create the weak event handlers from the class handling the event.

    1. Well, I’m not aiming for completeness as Daniel did. My solution is probably similar to his FastSmartWeakEvent (although I didn’t look at the exact implementation), with similar performance. Mine is available as a Nuget package, making it easier to use.

Leave a Reply to Joseph N. Musser II Cancel reply
