6
votes

Say you have a button on your form. You attached an anonymous function to button's Click event:

void Test()
{
  int x = 10;
  btn.Click += (sender, e) => { MessageBox.Show(x.ToString()); };
}

This works as expected and displays 10; means it can access local variables. My question is how and why? How does an anonymous function get access to the local context?

The actual problem I'm dealing with is that I need to upgrade (so to speak) this anonymous function to a regular function (event handler). But doing that means I'll lose access to the variable x. I can't pass it in as a parameter too because then I'll not be able to attach it to Click event (signature mismatch). I can work around this by creating global variables and all that, but how do anonymous functions make it possible to access things that were outside their scope?

1
Why do you need to create a regular function for this? If you could give more context, it would really help. For example, would it be enough just to be able to get the delegate? Because you can do that easily enough: EventHandler handler = (sender, e) => { ... }Jon Skeet
@JonSkeet: Part of a larger problem, where my attached property is not calling PropertyChangedCallback that I had declared and assigned anonymously. I suspect this could be due to the anonymous method going out of scope and thus get wiped by GC and so wanted to try with a regular function.dotNET
@dotNET: No, that won't be the problem. A delegate instance created via an anonymous function won't be garbage collected any faster than one created via a regular function. There are subtleties around unsubscription for events using anonymous functions but that's a different matter.Jon Skeet

1 Answers

15
votes

Half of the point of anonymous functions are that they can capture the context in which they're specified. It's extremely convenient to be able to do so - that's the "why" part.

The way the compiler does this is to create a new class in cases where it needs to. So your code would be converted to something like:

void Test()
{
    TestHelper helper = new TestHelper();
    helper.x = 10;

    btn.Click += helper.Method;
}

private class TestHelper
{
    public int x = 10;

    public void Method(object sender, EventArgs e)
    {
        MessageBox.Show(x.ToString());
    }
}

Every use of x within Test is converted into a use of helper.x for the appropriate instance. This is how variables with different lifetimes is covered too. For example, suppose you had a loop like this:

for (int i = 0; i < 10; i++)
{
    int x = i;
    // Use with some anonymous function
}

then it would create a new instance of TestHelper for each iteration of the loop... whereas if x had been declared outside the loop, there'd just be a single instance that all the anonymous functions would effectively share.

When it's just this that's captured, the compiler creates an instance method within the existing class instead of creating a helper class. When there are different scopes with potentially multiple anonymous functions which capture a variety of variables, things can get a lot more complicated, with some helper classes having references to instances of other helper classes, etc.