C# Lambdas and "this" variable scope
There is nothing wrong with using this
in a lambda, but as you mention, if you do use this
(or if you use it implicitly, by calling any nonstatic member function or using a nonstatic member variable) then the garbage collector will keep the object that this
refers to alive at least as long as the delegate is alive. Since you pass a lambda to Lazy
, this implies that the Repository
will be alive at least as long as the Lazy
object is alive (even if you never call Lazy.Value
).
To demystify it a bit, it helps to look in a disassembler. Consider this code:
class Foo {
static Action fLambda, gLambda;
int x;
void f() {
int y = 0;
fLambda = () => ++y;
}
void g() {
int y = 0;
gLambda = () => y += x;
}
}
The standard compiler changes this to the following (try to ignore the <>
extra angle brackets). As you can see, lambdas that use variables from inside the function body are transformed into classes:
internal class Foo
{
private static Action fLambda;
private static Action gLambda;
private int x;
private void f()
{
Foo.<>c__DisplayClass1 <>c__DisplayClass = new Foo.<>c__DisplayClass1();
<>c__DisplayClass.y = 0;
Foo.fLambda = new Action(<>c__DisplayClass.<f>b__0);
}
private void g()
{
Foo.<>c__DisplayClass4 <>c__DisplayClass = new Foo.<>c__DisplayClass4();
<>c__DisplayClass.<>4__this = this;
<>c__DisplayClass.y = 0;
Foo.gLambda = new Action(<>c__DisplayClass.<g>b__3);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
public int y;
public void <f>b__0()
{
this.y++;
}
}
[CompilerGenerated]
private sealed class <>c__DisplayClass4
{
public int y;
public Foo <>4__this;
public void <g>b__3()
{
this.y += this.<>4__this.x;
}
}
}
If you use this
, whether implicitly or explicitly, it becomes a member variable in the compiler-generated class. So the class for f()
, DisplayClass1
, does not contain a reference to Foo
, but the class for g()
, DisplayClass2
, does.
The compiler handles lambdas in a simpler manner if they don't reference any local variables. So consider some slightly different code:
public class Foo {
static Action pLambda, qLambda;
int x;
void p() {
int y = 0;
pLambda = () => Console.WriteLine("Simple lambda!");
}
void q() {
int y = 0;
qLambda = () => Console.WriteLine(x);
}
}
This time the lambdas don't reference any local variables, so the compiler translates your lambda functions into ordinary functions. The lambda in p()
does not use this
so it becomes a static function (called <p>b__0
); the lambda in q()
does use this
(implicitly) so it becomes a non-static function (called <q>b__2
):
public class Foo {
private static Action pLambda, qLambda;
private int x;
private void p()
{
Foo.pLambda = new Action(Foo.<p>b__0);
}
private void q()
{
Foo.qLambda = new Action(this.<q>b__2);
}
[CompilerGenerated] private static void <p>b__0()
{
Console.WriteLine("Simple lambda!");
}
[CompilerGenerated] private void <q>b__2()
{
Console.WriteLine(this.x);
}
// (I don't know why this is here)
[CompilerGenerated] private static Action CS$<>9__CachedAnonymousMethodDelegate1;
}
Note: I viewed the compiler output using ILSpy with the option "decompile anonymous methods/lambdas" turned off.