C# C“Lambdas和”;这";可变范围
我想知道我是否可以在C#lambda中使用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
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;
}