This post was migrated from Justin’s personal blog, 'Codethinked.com.' Views, opinions, and colorful expressions should be taken in context, and do not necessarily represent those of Simple Thread (and were written under the influence of dangerous levels of caffeination).

Today on Twitter Pete Brown mentioned that he had put up a post about using lambdas to set events in Silverlight. It is a really clean pattern, and it is useful in most places that you need to set events (this is a stripped down version of the code that Pete posted on his blog):

public class Test
{        
    public void LoadPolicyDetail()
    {
        var client = new IvcDataServiceClient();

        client.GetPolicyDetailCompleted += (s, e) =>
        {
            // Do something
        };

        client.GetPolicyDetailAsync();
    }
}

In this code you can see that we are creating a client class and then assigning a statement lambda to the event. The code inside of the event isn’t really important, and so I just removed it. Then we call “GetPolicyDetailsAsync” which will make an asynchronous call which when it completes invokes the “GetPolicyDetailCompleted” event.

Will It Leak?

The question is, since the method leaves asynchronously whether or not there are any resources leaked or there is any chance of something being garbage collected too early since the client and lambda are both going out of scope as soon as the method exits.

In order to get an answer to this we are going to have to consider how the lambda in this method is going to be generated. Since this lambda isn’t leaving the scope of this method, the C# compiler is going to emit it as a private static method on the test class like this:

[CompilerGenerated]
private static void <LoadPolicyDetail>b__0(object s, EventArgsSpecial e)
{
}

So all we are really doing is assigning this static method to the event handler. Since we are assigning this static method to the event handler then it doesn’t really matter what happens to the current instance of this class…but hold that thought. Let’s look at the program which runs this code:

class Program
{
    static void Main(string[] args)
    {
        var test = new Test();            
        test.LoadPolicyDetail();            
        Console.ReadLine();
    }
}

So we create the Test object call the method on it, and then just wait until the user hits a button in order to exit the program. So as you can see the “test” class is going to be garbage collected right after the “LoadPolicyDetail” method is called, but since the event was on the static method it will still be called.

Now the client class is a bit different, it doesn’t get garbage collected. But how? Doesn’t it go out of scope when we leave the “LoadPolicyDetail” method? Let’s look at how we implemented that class:

public class IvcDataServiceClient
{
    public event Action<Object, EventArgsSpecial> GetPolicyDetailCompleted;

    public void GetPolicyDetailAsync()
    {
        ThreadPool.QueueUserWorkItem(_ =>
        {
            Thread.Sleep(5000);
            GetPolicyDetailCompleted(this, new EventArgsSpecial());
            Console.WriteLine("Event Finished");
        });
    }
}

So, yes, the variable goes out of scope, but a reference is still held to the class by the thread that was spun up in the thread pool. This is because if you notice this lambda is referencing the instance by using “this” and “GetPolicyDetailCompleted”, and so it is created as an instance method on the “IvcDataServiceClient” class. Aaaaaaaah. Are you lost yet?

In the end it kinda feels like you are trying to stitch together the compiler magic in your head just to see if something is going to be kept alive or not. But how can we tell if something has been garbage collected? If we have a reference to it, then we know it won’t be collected. I bet you were wondering where Heisenberg came into this!

WeakReference To The Rescue

Well, it is a good thing that the .NET Framework team saw fit to bless us with the WeakReference class. This wonderful little class allows us to get a reference to an object which is “weak”, meaning that you can hold a reference but it won’t be counted as a reference by the garbage collector. This class has a property on it called “IsAlive” which returns a boolean that says whether or not the instance had been garbage collected.

So how can we use this to our advantage? Well, we can get a weak reference to the class that we expect to be collected, and then force a garbage collection. If the class is collected, then we know that we don’t have a leak. So in this case we could do this:

public class Test
{        
    public WeakReference LoadPolicyDetail()
    {
        var client = new IvcDataServiceClient();
        var weakReference = new WeakReference(client);

        client.GetPolicyDetailCompleted += (s, e) =>
        {
            // Do something
        };

        client.GetPolicyDetailAsync();
        return weakReference;            
    }
}

And so now we are going to return a weak reference to the client class, and in the calling program we can check this:

class Program
{
    static void Main(string[] args)
    {
        var test = new Test();            
        WeakReference wr = test.LoadPolicyDetail();            
        
        GC.Collect();
        Console.WriteLine(wr.IsAlive);
        Console.ReadLine();
        
        GC.Collect();
        Console.WriteLine(wr.IsAlive);
        Console.ReadLine();
    }
}

We get the weak reference and then force a collection, then we check to see if the client is still alive. Which of course it is. Then we pause until the user hits enter. We can then wait for the event to finish and we will see the “Event Finished” message appear. If all went well then we can force another collection and then “IsAlive” should return false. In this case, it does, and we can now know that what we are doing will not leak memory. If we got a true back, then that would mean that even though the event finished and we have no more references to the objects, somehow the instance survived a garbage collection. Time to start looking into why that is happening. And when that happens, you might need the big guns like WinDbg and SOS.

Summary

WeakReference provides a fairly low level way of determining if something is leaking. It is also a way in which you can reference other classes without actually holding a strong references to them. This is actually quite useful in a number of different instances. I hope that you found this post interesting, and maybe even added another simple tool to your tool chest.

8 Comments

Pete

Great post and walkthrough, Justin!

I’ve never had reason to play with WeakReference. I’ll need to check it out.

Pete

Reply
Justin Etheredge

@Roger Yeah, it was a horrible title. It sorta had something to do with not knowing whether something was leaking and getting a reference to it at the same time. I was trying to get too clever. 🙂

Reply
Niki

Maybe I missed something, but does the lambda behave any differently from a classic delegate here? The only difference I see is that it’s easier to unsubscribe the event if I registered a delegate instead of a lambda expression.

Reply
Justin Etheredge

@Niki Nope, you’re not missing anything here. This delegate, since it is locally scoped, will work in an almost identical way to a classic delegate. I say almost because in a normal situation it would have been likely that the delegate would have been assigned via an instance method, which would have caused a reference to the Test class to be held onto for the duration of the event.

The idea though was that there was some confusion on this, and WeakReference could be used to clear up any confusion.

Reply
Bart Czernicki

Justin,

I thought you can’t unsubscribe from an anonymous method or lamba expression. I thought that is a major difference between a classic delegate and the "inline way" of doing things.

Reply
Justin Etheredge

@Bart Nope, you can call the "GetInvocationList" method on an event to get references to all delegates assigned to it. You can then use those to unsubscribe each one. Also, if you had assigned the lambda to a variable, then you could use that to unsubscribe.

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *