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.