关闭是否涉及拳击?

Does closure involves boxing?

在我问完 and read this recommended article about closures之后,我开始问自己,在C#中做闭包是否涉及装箱。

正如文章中所说,这行代码将通过使 myVar 存在于它的范围之外来创建闭包:

public static Func<int,int> GetAFunc()
{
    var myVar = 1;
    Func<int, int> inc = delegate(int var1)
                            {
                                myVar = myVar + 1;
                                return var1 + myVar;
                            };
    return inc;
}

正如那里所解释的那样,编译器生成的对象 class 被实例化以在其范围之外携带此变量的值。我的问题来了:既然变量确实在栈上,使它成为对象的一部分,那不就意味着闭包涉及装箱了吗?

Since the variable is indeed located on stack, making it part of an object, doesn't mean that closures involves boxing?

那句话错的很多。让我们为您破除一些迷思。

(1) 值类型的变量不"go on the stack"。 生命周期短的变量进入堆栈。这个变量的生命周期短吗?不,它有任意长的寿命。那么它会进入堆栈吗?编号

(2)引用类型对象的字段不在栈中。它在堆上。为什么?因为,同样,一个字段有任意长的生命周期。

(3) 在堆上的变量中的整数不需要装箱。整数是否装箱取决于 integer 是否已转换为引用类型。 变量的位置无关紧要;重要的是变量的类型是引用类型还是值类型。

让我们看看您的代码:

public static Func<int,int> GetAFunc()
{
    var myVar = 1;
    Func<int, int> inc = delegate(int var1)
                            {
                                myVar = myVar + 1;
                                return var1 + myVar;
                            };
    return inc;
}

这段代码等价于:

private class Closure
{ 
  public int myVar;
  public int SomeFunction (int var1)
  {
    this.myVar = this.myVar + 1;
    return var1 + this.myVar;
  }
}
public static Func<int,int> GetAFunc()
{
    Closure locals = new Closure();
    locals.myVar = 1;
    Func<int, int> inc = locals.SomeFunction;
    return inc;
}

有没有把整数转换成引用类型的时候?不,所以,没有拳击。

但是请记住,避免装箱的要点是避免分配额外对象的成本。我们确实 分配了一个额外的对象:闭包!这里没有拳击处罚,但有因关门而产生的处罚。分配闭包会增加收集压力。当然,所有对本地的引用现在都必须通过额外的间接寻址。