C# Lambda 和“this”变量范围

2024-01-12

我想知道我是否可以使用thisC# lambda 中的关键字,尽管实际上我know我可以,但我想确保这不是一件坏事,或者稍后会产生微妙的问题。

阅读规则后lambda 的变量范围 http://msdn.microsoft.com/en-us/library/bb397687.aspx, 我理解了:

捕获的变量不会被垃圾回收,直到 引用它的委托超出了范围。

所以这让我假设一个对象实例(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";
    }
}

这会运行并给我正确的输出,因此访问this从 lambda 内部显然可以工作。我想检查的是:

  • 这是否遵循与局部变量相同的变量范围规则,这意味着this引用会保留在内存中,直到不再使用 lambda 吗?从我的小实验看来是这样,但如果有人能提供更多细节,我会很感兴趣。
  • 这是可取的吗?我不想稍后陷入这种模式可能导致问题的情况。

使用起来没有什么问题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;
    }
}

标准编译器将其更改为以下内容(尝试忽略<>额外的尖括号)。如您所见,使用函数体内变量的 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;
        }
    }

}

如果你使用this,无论是隐式还是显式,它都成为编译器生成的类中的成员变量。所以该类为f(), DisplayClass1,不包含对Foo,但是班级g(), DisplayClass2, does.

如果 lambda 不引用任何局部变量,编译器会以更简单的方式处理它们。所以考虑一些稍微不同的代码:

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

这次 lambda 不引用任何局部变量,因此编译器将 lambda 函数转换为普通函数。中的 lambdap()不使用this所以它变成了一个静态函数(称为<p>b__0);中的 lambdaq()确实使用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;
}

Note:我使用查看编译器输出ILSpy https://github.com/icsharpcode/ILSpy选项“反编译匿名方法/lambda”已打开off.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# Lambda 和“this”变量范围 的相关文章

随机推荐