是否需要引用变量才能包含在闭包中?

时间:2011-07-21 09:30:59

标签: c# javascript closures

在创建闭包时(在Javascript或C#中),闭包创建时范围内的所有变量是否都“封闭”在其中?或者只是新创建的方法中引用的变量?

示例C#代码:

private void Main() {
    var referenced = 1;
    var notReferenced = 2;  // Will this be enclosed?
    new int[1].Select(x => referenced);
}

示例Javascript代码:

    var referenced = 1;
    var notReferenced = 2;  // Will this be enclosed?
    var func = function () {
        alert(referenced);
    }

(通过使用Javascript闭包创建循环引用来阅读IE中的内存泄漏时,问题来了。http://jibbering.com/faq/notes/closures/#clMem
注意:使用“封闭”一词,我的意思是MSDN称之为“已捕获”。 (http://msdn.microsoft.com/en-us/library/bb397687.aspx

5 个答案:

答案 0 :(得分:18)

答案 1 :(得分:8)

这里有两个问题。将来,如果您有两个问题,可以考虑发布两个单独的问题。

  

在C#中创建闭包时,闭包创建时范围内的所有变量是否都“包含”在其中?或者只是新创建的方法中引用的变量?

这取决于您是否要求 dejure 事实答案。

首先,让我们澄清你的问题。我们将“外部变量”定义为局部变量,值参数(即,不是ref或out),“params”参数,或者在方法内部但在任何lambda或匿名方法之外发生的实例方法的“this”该方法中的表达式。 (是的,“这个”被归类为“外部变量”,即使它没有被归类为“变量”。这很不幸,但我已经学会了忍受它。)

在lambda或匿名方法表达式中使用的外部变量称为“捕获的外部变量”。

现在让我们重新提出问题:

  

在运行时创建与lambda或匿名方法对应的委托时, all 的生命周期是在委托创建时扩展到匹配(或超过)范围内的外部变量)代表的生命周期?或者只是捕获的外部变量的生命周期?

事实上,只延长了捕获的外部变量的生命周期。 De jure ,规范要求捕获的外部变量的生命周期被扩展,但禁止未捕获的外部变量的生命周期延长

阅读C#4规范的5.1.7,6.5.1和7.15.5节,了解所有细节。

Jon指出,我们并没有特别好地确保捕获的外部变量的生命周期最小化。我们希望有一天会做得更好。

答案 2 :(得分:5)

我只能在C#方面回答。

如果闭包实际上没有捕获变量,它将作为方法中的常规局部变量保留。如果它被捕获,它将仅被提升为合成类中的实例变量。 (当我谈到变量被“封闭”时,我假设你正在谈论这个。)

但是,请注意,如果两个lambda表达式各自捕获相同作用域的不同变量,那么在当前实现中两个 lambdas将使用相同的合成类:

private Action Foo() {
    var expensive = ...;
    var cheap = ...;
    Action temp = () => Console.WriteLine(expensive);
    return () => Console.WriteLine(cheap);
}

此处返回操作仍然会使“昂贵”引用保持活动状态。所有这些都是Microsoft C#4编译器的实现细节,但将来可能会发生变化。

答案 3 :(得分:3)

我只能回答有关C#:

的问题

不,它不会被封闭,因为它不需要。

我解释“它会被封闭吗?”在这种情况下,如下所示:

是否会在为lambda表达式生成的闭包内捕获它,即自动生成的类中是否有一个包含此变量值的字段?

声明:
这是一个实现细节。我的答案适用于MS针对您的特定情况当前实现的C#编译器。它可能与Mono编译器或更新版本的MS编译器不同。或者 - 正如Jon的回答所示 - 对于一个更复杂的例子,它可能会有所不同。

答案 4 :(得分:1)

  

在创建闭包时(在Javascript或C#中),闭包创建时范围内的所有变量是否都“封闭”在其中?或者只是新创建的方法中引用的变量?

我只能回答有关JavaScript的问题。

这是特定于实现的。一些引擎包含所有变量。一些引擎优化并且只包含引用的变量。

我知道Chrome会优化并且只包含它关心的变量。

进一步阅读:

似乎这个问题源于对旧的IE内存泄漏问题的担忧。这在现代浏览器中不是问题。

通过在EcmaScript和Host对象之间进行循环引用,很好地了解了IE8中仍然存在的内存泄漏:

IE8 memory leak

为了澄清有关闭包的IE内存泄漏问题,它主要是关于Host对象(DOM)和JavaScript之间的循环引用。因此,除非您使用DOM(el.attachEvent或类似的东西)

,否则此问题不存在