Simple, not putting in the stack.
The delegate infrastructure that is the basic mechanism of lambda allows the use of what is called enclosure (closure). In other words, it traps a state within the function and takes it with it wherever it goes. It is not that the precise data becomes a reference, but it will need to have some reference to some object in the heap where you have the die.
So the compiler generates an internal code that deals with what would normally be on stack and puts in the heap which is a place where data has indefinite lifespan.
In this case, the lifetime of this data, accessed by a variable, is linked to the lifetime of the anonymous function. In the case of . NET its destruction will occur sometime after a garbage collection is fired in the generation in which it is.
So while the lambda exist the variable will exist, will not be released.
Note that there may be some unexpected effect, especially on ties, since the local value may be dissociated from the value of the lambda. A common example is a loop go up to 10 and have the lambda after, even if the person does not realize that it is after, and the value will always be 10 and not 1 to 10 as the person would expect. A lambda is a mechanism used to Lazy Evaluation.
How it is internally
I couldn’t name anyone less than the guy who wrote the compiler code that does this, Ladies and gentlemen: Eric Lippert.
class C1 {
Func<int, int, int> M() => (x, y) => x + y;
}
Turns into:
class C1 {
static Func<int, int, int> theFunction;
static int Anonymous(int x, int y) => x + y;
Func<int, int, int> M() {
if (C1.theFunction == null) C1.theFunction = C1.Anonymous;
return C1.theFunction;
}
}
Real code on Sharplab.
class C2 {
static int counter = 0;
int x = counter++;
Func<int, int> M() => y => this.x + y;
}
Turns into:
class C2 {
static int counter = 0;
int x = counter++;
int Anonymous(int y) => this.x + y;
Func<int, int> M() => this.Anonymous;
}
Real code on Sharplab.
class C3 {
static int counter = 0;
int x = counter++;
Func<int> M(int y) => () => x + y;
}
Turns into:
class C3 {
class Locals {
public C3 __this;
public int __y;
public int Anonymous() => this.__this.x + this.__y;
}
Func<int> M(int y) {
var locals = new Locals();
locals.__this = this;
locals.__y = y;
return locals.Anonymous;
}
}
Real code on Sharplab.
I put in the Github for future reference.