Ответ 1
Нет ничего плохого в использовании this
в лямбда, но, как вы говорите, если вы используете this
(или если вы используете его неявно, вызывая любую нестатистическую функцию-член или используя нестатические переменные-члены), тогда сборщик мусора будет хранить объект, который this
относится к живым, по крайней мере, до тех пор, пока делегат жив. Поскольку вы передаете лямбда на Lazy
, это означает, что Repository
будет жив как минимум до тех пор, пока объект Lazy
жив (даже если вы никогда не звоните Lazy.Value
).
Чтобы немного ослабить его, он помогает разобраться в дизассемблере. Рассмотрим этот код:
class Foo {
static Action fLambda, gLambda;
int x;
void f() {
int y = 0;
fLambda = () => ++y;
}
void g() {
int y = 0;
gLambda = () => y += x;
}
}
Стандартный компилятор изменит это на следующее (попробуйте игнорировать дополнительные угловые скобки <>
). Как вы можете видеть, лямбды, которые используют переменные изнутри тела функции, преобразуются в классы:
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;
}
}
}
Если вы используете this
, неявно или явно, он становится переменной-членом в классе, генерируемом компилятором. Таким образом, класс для f()
, DisplayClass1
не содержит ссылки на Foo
, но класс для g()
, DisplayClass2
, делает.
Компилятор обрабатывает lambdas более простым способом, если они не ссылаются на какие-либо локальные переменные. Поэтому рассмотрим несколько немного отличающийся код:
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);
}
}
В этот раз lambdas не ссылаются на какие-либо локальные переменные, поэтому компилятор переводит ваши лямбда-функции в обычные функции. Лямбда в p()
не использует this
, поэтому она становится статической функцией (называемой <p>b__0
); lambda в q()
использует this
(неявно), поэтому он становится нестатической функцией (называемой <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;
}
Примечание. Я просмотрел вывод компилятора с помощью ILSpy с опцией "декомпилировать анонимные методы /lambdas " выключено.