C# C“Lambdas和”;这";可变范围

C# C“Lambdas和”;这";可变范围,c#,lambda,scope,this,C#,Lambda,Scope,This,我想知道我是否可以在C#lambda中使用this关键字,虽然我知道我可以,但我想确保这不是一件坏事,或者以后会产生微妙的问题 在阅读了有关的规则后,我发现: 捕获的变量在 引用它的委托超出范围 因此,我假设还将捕获一个对象实例(this)。为了测试这一点,我编写了这个精心设计的示例,这正是我想在实际代码中大致实现的目标——用LINQPad编写,因此我有Dump()方法调用: void Main() { Repository repo=新存储库(); 人=回购人(1); person.ID.Du

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

在阅读了有关的规则后,我发现:

捕获的变量在 引用它的委托超出范围

因此,我假设还将捕获一个对象实例(
this
)。为了测试这一点,我编写了这个精心设计的示例,这正是我想在实际代码中大致实现的目标——用LINQPad编写,因此我有
Dump()
方法调用:

void Main()
{
Repository repo=新存储库();
人=回购人(1);
person.ID.Dump(“person ID-赋值”);
person.Name.Dump(“人名-惰性创建”);
}
班主任
{
公众人物(懒名)
{
this.name=名称;
}
公共int ID{get;set;}
私人懒名;
公共字符串名
{
获取{return name.Value;}
}
}
类存储库
{
公众人物GetPerson(内部id)
{
//设置person以延迟加载名称值
人=新人(
新懒汉(

()=>this.GetName()/在lambdas中使用
this
是绝对正确的,但是有一些东西你应该记住:

  • 将保留在内存中,直到不再使用lambda
  • 如果你没有通过lambda“与
    ”以外的课程,那么你将不会面临问题
  • 如果您确实在类外通过lambda“with
    this
    ”,那么您应该记住,
    GC
    将不会收集您的类,直到剩下对lambda的引用

与您的用例相关的是,您应该记住,
存储库
实例在它创建的人被使用之前不会被
GC
收集。

虽然在这样的lambda中使用
这个
是正确的,但您只需要知道
存储库
对象不会是垃圾收集的直到您的
Person
对象可被垃圾回收

您可能希望有一个字段来缓存lambda的结果,一旦它被惰性填充,就释放lambda,因为您不再需要它了

比如:

private Lazy<string> nameProxy; 
private string name;
public string Name 
{ 
  get 
  {
    if(name==null)
    {
      name = nameProxy.Value;
      nameProxy = null;
    }
    return name;
  } 
} 
私有代理;
私有字符串名称;
公共字符串名
{ 
得到
{
if(name==null)
{
name=nameProxy.Value;
nameProxy=null;
}
返回名称;
} 
} 

在lambda中使用
this
没有错,但是正如您所提到的,如果您确实使用
this
(或者如果您隐式使用它,则通过调用任何非静态成员函数或使用非静态成员变量)然后,垃圾收集器将至少在代理处于活动状态时保持
引用的对象处于活动状态。因为您将lambda传递给
Lazy
,这意味着
存储库
将至少在
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;
        }
    }

}
这次lambda不引用任何局部变量,因此编译器将lambda函数转换为普通函数。
p()
中的lambda不使用
This
,因此它成为一个静态函数(称为
b_u0
);
q()
中的lambda使用
This
(隐式)因此它变成了一个非静态函数(称为
b_uu2
):

公共类Foo{
私人静动平台,qLambda;
私人INTX;
私有void p()
{
Foo.pLambda=新动作(Foo.b_0);
}
私有void q()
{
Foo.qLambda=新操作(this.b_uuu2);
}
[CompilerGenerated]私有静态voidb_u0()
{
Console.WriteLine(“简单lambda!”);
}
[CompilerGenerated]私有void b__2()
{
Console.WriteLine(this.x);
}
//(我不知道这是为什么)
[CompilerGenerated]私有静态操作CS$9_uCachedAnonymousMethodDelegate1;
}

注意:我使用选项“反编译匿名方法/lambdas”查看编译器输出关闭

回答很好。解释得很好。在那里看到反汇编代码以真正了解发生了什么,这真的很酷。我同意,看到您的代码实际转化为什么非常有帮助。我在Reflector中看到过这样的代码,但之前还没有真正理解它是如何工作的,所以感谢您的解释在过去的两个小时里,我一直在尝试在ILSpy中搜索“反编译匿名方法/lambda”的等价物。我认为这一部分需要更好地突出显示。谢谢。一个极好的答案。我试图使用ILSpy解码委托闭包。但从未使用过显示类。您对“反编译匿名方法/lambdas关闭”“为我工作,看看编译器如何处理变量范围。伟大的帮助”QWWITTY。谢谢。一个有趣的想法。我可以想到其他的方法来解决我的实际情况,如果我真的想在代码中避免<代码>这个<代码>,但是我也可以考虑这个方法,谢谢。
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);
    }
}
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;
}