I am wondering whether I can use the this
keyword inside a C# lambda, although actually I know that I can but I want to make sure that this isn\'t a bad th
It is absolutelly fine to use this
in lambdas, but there is some stuff you should keep in mind:
this
will be kept in memory until the lambda is not used anymorethis
" outside your class, then you will not face problemsthis
" outside your class, then you should remember, that your class will not be collected by the GC
until there are references to the lambda left.And related to your use-case, you should keep in mind that the Repository
instance will never be collected by the GC
until people it created are in use.
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.
While it is correct to use this
in a lambda like that, you just need to be aware that your Repository
object will not be garbage collectable until your Person
object is garbage collectable.
You might want to have a field to cache the result from your lambda, and once it is Lazy filled, release the lambda since you do not need it anymore.
Something like:
private Lazy<string> nameProxy;
private string name;
public string Name
{
get
{
if(name==null)
{
name = nameProxy.Value;
nameProxy = null;
}
return name;
}
}