在C#中由lambda创建的委托的生命周期是多少?

Ste*_*ris 43 c# lambda delegates

Lambdas很不错,因为它们提供简洁和局部性以及额外形式的封装.而不必编写只使用一次可以使用lambda的函数.

在想知道它们是如何工作的时候,我直觉地认为它们可能只创建过一次.这激发了我创建一个解决方案,该解决方案允许通过使用lambda作为其创建范围的标识符,将类成员的范围限制在私有范围之外.

这种实现虽然可能有些过分(仍在研究它),但可以证明我的假设是正确的.

一个较小的例子:

class SomeClass
{
    public void Bleh()
    {
        Action action = () => {};
    }

    public void CallBleh()
    {
        Bleh();  // `action` == {Method = {Void <SomeClass>b__0()}}
        Bleh();  // `action` still == {Method = {Void <SomeClass>b__0()}}
    }
}
Run Code Online (Sandbox Code Playgroud)

lambda会返回一个新实例,还是保证总是相同?

Jon*_*eet 30

无论如何都无法保证.

从我记得的当前MS实现:

  • 不捕获任何变量的lambda表达式是静态缓存的
  • 只捕获"this"的lambda表达式可以基于每个实例捕获,但不是
  • 捕获局部变量的lambda表达式无法缓存
  • 两个具有完全相同的程序文本的lambda表达式没有别名; 在某些情况下,它们可能是,但是解决它们可能存在的情况会非常复杂
  • 编辑:正如Eric在评论中指出的那样,您还需要考虑为泛型方法捕获的类型参数.

编辑:C#4规范的相关文本见6.5.1节:

将具有相同(可能为空)的捕获的外部变量实例集的语义相同的匿名函数转换为相同的委托类型是允许(但不是必需的)返回相同的委托实例.这里使用术语相同的术语来表示在所有情况下,在给定相同参数的情况下,匿名函数的执行将产生相同的效果.

  • @Steven:我向你保证,如果你创建"()=>这个"两次,它就不会*返回同一个委托.如果那是你得到的结果,你做错了什么.您是如何尝试比较代表的平等?**你知道代表有价值,而不是参考平等,对吗?** (3认同)

Eri*_*ert 30

根据你在这里提出的问题以及你对Jon的答案的评论,我认为你混淆了很多事情.为了确保清楚:

  • 支持给定lambda的委托的方法总是相同的.
  • 方法是用来备份该词汇出现两次"相同的"拉姆达委托许可是相同的,但在实践中是不是在我们的实施是相同的.
  • 为给定lambda创建的委托实例可能会也可能不会始终相同,具体取决于编译器对缓存它的智能程度.

所以如果你有类似的东西:

for(i = 0; i < 10; ++i)
    M( ()=>{} )
Run Code Online (Sandbox Code Playgroud)

然后每次调用M时,都会获得相同的委托实例,因为编译器是智能的并且生成

static void MyAction() {}
static Action DelegateCache = null;

...
for(i = 0; i < 10; ++i)
{
    if (C.DelegateCache == null) C.DelegateCache = new Action ( C.MyAction )
    M(C.DelegateCache);
}
Run Code Online (Sandbox Code Playgroud)

如果你有

for(i = 0; i < 10; ++i)
    M( ()=>{this.Bar();} )
Run Code Online (Sandbox Code Playgroud)

然后编译器生成

void MyAction() { this.Bar(); }
...
for(i = 0; i < 10; ++i)
{
    M(new Action(this.MyAction));
}
Run Code Online (Sandbox Code Playgroud)

每次都会使用相同的方法获得新的委托.

允许编译器(但实际上此时不生成)生成

void MyAction() { this.Bar(); }
Action DelegateCache = null;
...
for(i = 0; i < 10; ++i)
{
    if (this.DelegateCache == null) this.DelegateCache = new Action ( this.MyAction )
    M(this.DelegateCache);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果可能,您将始终获得相同的委托实例,并且每个委托都将由相同的方法支持.

如果你有

Action a1 = ()=>{};
Action a2 = ()=>{};
Run Code Online (Sandbox Code Playgroud)

然后在实践中,编译器将其生成为

static void MyAction1() {}
static void MyAction2() {}
static Action ActionCache1 = null;
static Action ActionCache2 = null;
...
if (ActionCache1 == null) ActionCache1 = new Action(MyAction1);
Action a1 = ActionCache1;
if (ActionCache2 == null) ActionCache2 = new Action(MyAction2);
Action a2 = ActionCache2;
Run Code Online (Sandbox Code Playgroud)

但是,允许编译器检测到两个lambda是相同的并生成

static void MyAction1() {}
static Action ActionCache1 = null;
...
if (ActionCache1 == null) ActionCache1 = new Action(MyAction1);
Action a1 = ActionCache1;
Action a2 = ActionCache1;
Run Code Online (Sandbox Code Playgroud)

那现在清楚了吗?

  • @Jenix:"允许"我的意思是允许C#编译器的作者编译你的程序,使得它在编译器作者的判断下返回"true"或"false".您不能*依赖于编译器具有一个或另一个行为因素,因为这被记录为允许随时更改. (2认同)