【问题标题】:Does closure involves boxing?关闭是否涉及拳击?
【发布时间】:2016-06-22 20:32:05
【问题描述】:

在我问过this question 并阅读了这个推荐的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;
}

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

【问题讨论】:

  • 变量位于堆栈上。 C# 编译器重写您的代码,myVar 成为类的一个字段。任何地方都没有拳击。使用 ildasm.exe 亲自查看。
  • 当值类型是另一种类型的字段时,没有装箱...您可能需要再次查看为闭包生成的代码。

标签: c# closures boxing


【解决方案1】:

既然变量确实在栈上,让它成为对象的一部分,不就意味着闭包涉及装箱吗?

那句话中的错误数量很大。让我们解除您的一些神话。

(1) 值类型的变量不会“入栈”。 生命周期短的变量进入堆栈。这个变量的生命周期很短吗?不,它有任意长的寿命。那么它会进入堆栈吗?没有。

(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;
}

是否有任何时间将整数转换为引用类型?不。所以,不要拳击。

但是请记住,避免装箱的目的是避免分配额外对象的成本。我们确实分配了一个额外的对象:闭包!这里没有拳击处罚,但由于关闭而受到处罚。分配封盖会增加收集压力。当然,所有对本地的引用现在都必须经过额外的间接处理。

【讨论】:

  • 您说得对,先生!我可以就这个问题再问一个问题吗?它不涉及装箱,因为它是在编译时生成的“闭包”类,因此变量不是首先在堆栈上分配,然后再移动到堆中。我理解对了吗?
  • @meJustAndrew 不。它不涉及装箱,因为值类型永远不会转换为引用类型。栈/堆与这个概念完全没有关系。
  • 我对拳击有一定的了解,没错。我认为当值类型转换为引用类型时会发生这种情况。但是关于确认号 1,为什么值类型变量不会像我认为的那样进入堆栈,根据this article from msdn,它说“值类型将其内容存储在分配在堆栈上的内存中”。我知道这篇文章可能已经过时了,我现在很欣赏你的回答,我不想通过不断提问来表现出不尊重。
  • @meJustAndrew 这是一篇不幸的文章,因为它对堆栈/堆语义做出了错误的概括。正如 Eric 所说,存储位置与存储的生命周期有关,而不是存储在其中的类型。
  • 那篇文章是我的克星。它误导了很多人。堆栈用于值类型的想法很愚蠢。堆栈用于短期存储。如果存储需要长时间存在,那么无论存储是否包含引用或值,它都必须在堆上!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-21
  • 2017-04-07
  • 1970-01-01
  • 2010-09-15
  • 1970-01-01
相关资源
最近更新 更多