C#Lambdas和"this"变量范围

Pet*_*nks 13 c# lambda scope this

我想知道我是否可以this在C#lambda中使用关键字,虽然实际上我知道我可以,但我想确保这不是一件坏事或稍后会产生微妙的问题.

阅读了关于lambda的变量范围的规则,我可以看到:

在引用它的委托超出范围之前,捕获的变量不会被垃圾收集.

所以这导致我假设this也会捕获一个对象实例().为了测试这个,我编写了这个人为的例子,这是我想要在我的真实代码中实现的目标 - 用LINQPad编写,因此为什么我有Dump()方法调用:

void Main()
{
    Repository repo = new Repository();
    Person person = repo.GetPerson(1);

    person.ID.Dump("Person ID - Value Assigned");
    person.Name.Dump("Person Name - Lazily Created");
}

class Person
{
    public Person(Lazy<string> name)
    {
        this.name = name;
    }

    public int ID { get; set; }

    private Lazy<string> name;
    public string Name
    {
        get { return name.Value; }
    }
}

class Repository
{
    public Person GetPerson(int id)
    {
        // Setup person to lazily load a name value
        Person person = new Person(
            new Lazy<string>(
                () => this.GetName()    // <--- This I'm not sure on...
            )
        );
        person.ID = id;
        return person;
    }

    public string GetName()
    {
        return "John Smith";
    }
}
Run Code Online (Sandbox Code Playgroud)

这运行并给我正确的输出,因此this从lambda中访问清楚地工作.我想要检查的是:

  • 这是否遵循与局部变量相同的变量范围规则,这意味着this引用将保留在内存中,直到lambda不再使用为止?从我的小实验看起来如此,但如果有人能提供进一步的细节,我会感兴趣.
  • 这是可取的吗?我不想在以后这种模式可能导致问题的情况下陷入困境.

Qwe*_*tie 14

this在lambda中使用没有任何问题,但正如你所提到的,如果你确实使用this(或者你通过调用任何非静态成员函数或使用非静态成员变量隐式使用它),那么垃圾收集器将保留this引用的对象至少只要代表还活着就活着.由于你传递一个lambda 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

标准编译器将此更改为以下内容(尝试忽略<>额外的尖括号).如您所见,使用函数体内部变量的lambda将转换为类:

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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

如果使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这次lambda不引用任何局部变量,因此编译器将lambda函数转换为普通函数.lambda in p()不使用,this因此它成为一个静态函数(称为<p>b__0); lambda in 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;
}
Run Code Online (Sandbox Code Playgroud)

:我认为使用编译器输出ILSpy与选项"反编译匿名方法/ lambda表达式"关.