Los Techies : Blogs about software and anything tech!

Interesting (good) Behavior of Closures


For some reason, I didn't think this would work, but it does:

            Customer c = null;
            Func<string> func = ()=>c.LookupName;
            c = new Customer {LookupName = "First"};
            System.Diagnostics.Debug.WriteLine(func());

I would've expected an NRE in the WriteLine because the 'c' reference (null) would've been packaged up by the closure.  But apparently it packages up the reference to the reference also so that if the 'c' variable value changes, so does the closure's reference to it. 

The output is not an NRE, but rather "First".

I was curious what it would do with valid references. Consider the following example:

            Customer c = new Customer{LookupName="First"};
            Func<string> func = ()=>c.LookupName;
            c = new Customer {LookupName = "Second"};
            System.Diagnostics.Debug.WriteLine(func());

The output is, as you would expect, "Second".

One last stretch here, what about stack-based value types:

            int i = -1;
            Func<int> func = ()=>i;
            i = 99;
            System.Diagnostics.Debug.WriteLine(func());

The output is 99.

The way it works under the hood is that the compiler doesn't actually create a new stack variable called 'i' (like it would normally), it creates a new class called <>c__DisplayClass2d in my case. Well, that's hard to type, so let's just call it FancyClass:

            public class FancyClass{
                public int i;

                public int GetI(){
                    return i;
                }
            }

Then, it re-writes -- rather it compiles slightly different IL -- the code above (the int i = -1 example) like this:

            FancyClass c = new FancyClass();
            c.i = -1;
            Func<int> func = ()=>c.i;
            c.i = 99;
            System.Diagnostics.Debug.WriteLine(func());

Seen like this, it seems a bit more obvious.

Many of you are probably saying "Duh!", but this went against my understanding of how closures package up their context. 

Kick It on DotNetKicks.com
Posted Mar 08 2008, 10:42 PM by chadmyers
Filed under: ,

Comments

Dew Drop - March 10, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - March 10, 2008 | Alvin Ashcraft's Morning Dew
on 03-10-2008 7:01 AM

Pingback from  Dew Drop - March 10, 2008 | Alvin Ashcraft's Morning Dew

Jon Scolamiero wrote re: Interesting (good) Behavior of Closures
on 03-11-2008 1:33 PM

I like this behavior as it lets us do some setup early on in a method without worrying that the variable my not be instantiated yet.

Simone Busoli wrote re: Interesting (good) Behavior of Closures
on 03-14-2008 3:15 PM

Hi Chad, yeah, that's a cool behavior, and has somewhat become intuitive, since as lambda - or an anonymous method - are pointers to methods, not actually code which gets executed, therefore they create a closure around the variables of the environment and actually use them only when they are invoked.

That's why, when you create loops which call anonymous methods, at times you need to declare an inner variable which disconnects from the outer closure and keeps the right value of the variable for each iterations, and the following example usually doesn't work as expected:

var handle = new ManualResetEvent(false);

for(int i = 0; i < 1000000; i++)

   ThreadPool.QueueUserWorkItem(() =>  

   {

       handle.WaitOne();

       Console.WriteLine(i);

   });

handle.Set();

PS. Wrote it without the compiler, I'm not sure it does, but you get the idea.

Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems