The problem

At a recent C# Xamarin meetup (I’m a meetup junky) a statement was made from a very knowledgeable individual – when a lambda expression is wired up to an event if a reference is not created to that lambda there is in no way it can be unhooked from the event. Thus, a resource leak  has been created that cannot be rectified. This reference of course needs to be on the heap. I was familiar with the leaking lambda problem though even now I occasionally forget to release them – it’s an easy thing to do. I was however skeptical of the statement – the resource can’t be recovered – this is managed code after all. We discussed the usage of reflection and hunting down the Multicast delegates and he said he had gone that route and a reference to the wayward lambda could not be found. I had written a code project article a few years prior regarding reflection, delegates, and events. Lambdas however had not arrived on the scene at the time. Now I have enormous respect for this individual but still I couldn’t shake the feeling that there was a solution. I went home and jumped on the internet trolling for a solution (stack overflow you snarky bitch). Nothing. Still, I knew it could be done and I set to task. Below and attached is my solution.

The target event

First off we need an event to subscribe and unsubscribe to. We’ll define a very simple signature for our event – it simply passes an integer value to our subscribers when fired…

and the corresponding event declartion

We are good to go. Next we’ll add some subscribers – named and anonymous – and then remove them using reflection.

Unsubscribing via reflection

We begin by adding a subscription to the event using a non-anonymous function. First we declare a named function to subscribe

Next we subscribe our named function to our event. Assuming the class that owns the event is called EventSource the code is as follows.

In addition to the named function we’ll need to add a Lambda subscriber. A simple function that prints out to the command line will do.

Now let’s use reflection to unhook this event from it’s subscribers. Armed with the knowledge that each event on an object generates a corresponding private field we take the steps below.

From the target object we need to extract the events and their corresponding fields and pair them off…

Once we have events paired off with their fields we cycle through all the fields, get their associated MulticastDelegate objects, and unsubscribe each subscriber that appears in the MulitcastDelegate’s invovation list

The need for pairing the events and the fields is that the field holds the list of subscribers – the MulticastDelegate – but the unsubcribe method, “RemoveEventHandler”, is a method on the event object.

Lambda Caveat

The target event has now been purged of subscribers – even those that we’re declared as anonymous. There is a catch though – a potential leak that is insidious in nature. To understand the problem we need to delve into how Lambda’s are allocated and stored within our program. They may be anonymous but they need to reside somewhere. When a Lambda is created a private field is generated and attached to our event source’s type. In the case of our example the type is “EventSource”. As we know static fields are not subject to garbage collection. If a given Lambda instance should reference a variable (e.g. create a closure) there is a very strong potential of creating a resource leak. To rectify this I see two alternatives.

A) Define a “magic string” style input such that when broadcast to subscribers they will give up any references they acquired during instantiation. Far from ideal.

B) Cycle through the private static fields of the event source type looking for those that derive from Delegate. For each field encountered null out it’s reference. A shotgun approach. The code to accomplish this is as follows…

A final note for completeness: For the purposes of this article I’ve treated anonymous functions and Lambdas as one in the same. In actual fact there is a very subtle difference. I won’t go into it here but for those that are interested please see this blog post.

The full sample code that served as the basis for this article can be found here