C#Lambda和"this"可变范围 [英] C# Lambdas and "this" variable scope

查看:128
本文介绍了C#Lambda和"this"可变范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 thing or will produce subtle issues later on.

已经阅读了 lambda的可变范围上的规则,我可以看到:

Having read the rules on variable scope for lambdas, I can see that:

捕获的变量将不会被垃圾收集,直到 引用它的委托超出范围.

A variable that is captured will not be garbage-collected until the delegate that references it goes out of scope.

因此,这使我假设一个对象实例(this)也将被捕获.为了测试这一点,我编写了这个人为设计的示例,这是我想在我的真实代码中大致针对的示例-用LINQPad编写,因此为什么要使用Dump()方法调用:

So this leads me to assume that an object instance (this) will also be captured. To test this I wrote this contrived example which is what I want to approximately aim for in my real code - written in LINQPad, hence why I have the Dump() method calls:

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

这会运行并为我提供正确的输出,因此从lambda中访问this显然很有效.我想检查的是:

This runs and gives me the correct output so accessing this from within a lambda clearly works. What I am wanting to check though is:

  • 这是否遵循与局部变量相同的变量作用域规则,这意味着this引用将保留在内存中,直到不再使用lambda为止?从我的小实验中可以看出来,但如果有人可以提供更多详细信息,我将很感兴趣.
  • 这是明智的吗?我不想稍后再讨论这种模式可能引起问题的情况.
  • Does this follow the same variable scope rules as local variables, meaning that the this reference is kept in memory until the lambda is not used anymore? It would appear so from my little experiment but if anyone can give further details I'd be interested.
  • Is this advisable? I do not want to get into the situation later where this pattern could cause problems.

推荐答案

在lambda中使用this并没有错,但是正如您提到的,如果您确实使用this(或者隐式使用,通过调用任何非静态成员函数或使用非静态成员变量),则垃圾收集器将至少在代理处于活动状态的情况下使this引用的对象保持活动状态.由于您将lambda传递给Lazy,因此这意味着Repository至少在Lazy对象处于活动状态时仍处于活动状态(即使您从未调用Lazy.Value).

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

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

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

}

如果使用this(无论是隐式还是显式),它将成为编译器生成的类中的成员变量.因此,f()DisplayClass1的类不包含对Foo的引用,但是g()DisplayClass2的类却包含.

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.

如果lambda不引用任何局部变量,则它们将以更简单的方式处理它们.因此,请考虑一些稍有不同的代码:

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

这一次,lambda不会引用任何局部变量,因此编译器会将您的lambda函数转换为普通函数. p()中的lambda不使用this,因此它成为静态函数(称为<p>b__0); q()中的lambda确实使用了this(隐式),因此它成为非静态函数(称为<q>b__2):

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

注意:我使用 ILSpy 查看了编译器的输出,并带有以下选项: 关闭关闭了反编译匿名方法/lambdas".

Note: I viewed the compiler output using ILSpy with the option "decompile anonymous methods/lambdas" turned off.

这篇关于C#Lambda和"this"可变范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆