封闭是否涉及拳击?

meJ*_*rew 0 c# boxing 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;
}
Run Code Online (Sandbox Code Playgroud)

正如那里所解释的,编译器生成的类的对象被实例化以在该范围之外携带该变量的值.这是我的问题:由于变量确实位于堆栈上,使其成为对象的一部分,并不意味着闭包涉及拳击?

Eri*_*ert 8

由于变量确实位于堆栈上,使其成为对象的一部分,并不意味着闭包涉及拳击?

该句中的错误数量很大.让我们消除一些神话.

(1)值类型的变量不会"进入堆栈". 生命周期较短的变量会进入堆栈.这个变量的寿命很短吗?不,它具有任意长的寿命.它是如何进入堆栈的?没有.

(2)引用类型的对象的字段不在堆栈上.它在堆上.为什么?因为,一个领域具有任意长的寿命.

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

那么让我们来看看你的代码:

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

这段代码是等效的:

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;
}
Run Code Online (Sandbox Code Playgroud)

当整数转换为引用类型时,有没有时间?不,所以,没有拳击.

但是要记住,避免装箱的重点是避免分配额外物品的成本.我们确实分配了一个额外的对象:关闭!这里没有拳击惩罚,但由于关闭而有罚款.分配封闭物会增加收集压力.当然,所有对本地的引用现在必须经过额外的间接.

  • @meJustAndrew这是一篇不幸的文章,因为它对堆栈/堆语义做出了错误的概括.正如Eric所说,存储位置与存储的生命周期有关,而与存储在其中的类型无关. (2认同)
  • 那篇文章是我的克星.它误导了这么多人.堆栈用于值类型的概念是愚蠢的; 堆栈用于*短期存储*.如果存储需要存活很长时间,那么无论存储是包含引用还是值,它都必须在堆上运行! (2认同)