Discrete Anonymous methods sharing a class?

Solution 1:

Yes, the MS implementation of anonymous methods effectively creates one hidden class per level of scope that it needs to capture variables from, and captures all the relevant variables from that scope. I believe this is done for the sake of simplicity, but it can indeed increase the lifetime of some objects unnecessarily.

It would be more elegant for each anonymous method to only capture the variables it was actually interested in. However, this could make life considerably more complicated... if one anonymous method captured x and y, one captured x and one captured y, you'd need three classes: one for capturing x, one for capturing y, and one for composing the two (but not just having two variables). The tricky bit is that for any single variable instantiation, that variable needs to live in exactly one place so that everything which refers to it sees the same value, whatever changes it.

This doesn't violate the spec in any way, but it could be considered unfortunate - I don't know whether it's actually bitten people in real life, but it's certainly possible.

The good news is that if the C# team decide to improve this, they should be able to do so in an entirely backwardly compatible way, unless some muppets are relying on lifetimes being extended unnecessarily.

Solution 2:

Jon is of course right. The problem that this usually causes is:

void M()
{
    Expensive e = GetExpensive();
    Cheap c = GetCheap();
    D longLife = ()=>...c...;
    D shortLife = ()=>...e...;
    ...
}

So we have an expensive resource whose lifetime now depends on the lifetime of longLife, even though shortLife is collected early.

This is unfortunate, but common. The implementations of closures in JScript and VB have the same problem.

I'd like to solve it in a hypothetical future version of C# but I make no guarantees. The obvious way to do it is to identify equivalence classes of closed-over variables based on which lambdas they are captured by, and generate closure classes one per equivalence class, rather than a single closure class.

There also might be things we could do with analysis of what closed-over variables are written to. As Jon notes, we are restricted at present by our need to capture variables rather than values. We could be more flexible in our code generation strategy if we identified variables that are never written to after the closure is created, and make those into closed-over values rather than closed-over variables.