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

C# 是否需要引用变量才能将其包含在闭包中?,c#,javascript,closures,C#,Javascript,Closures,创建闭包时(在Javascript或C#中),闭包创建时作用域中的所有变量是否都“包含”在闭包中?或者只是在新创建的方法中引用的变量 示例C#代码: Javascript代码示例: var referenced = 1; var notReferenced = 2; // Will this be enclosed? var func = function () { alert(referenced); } (当我读到关于IE中通过使用Java

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

示例C#代码:

Javascript代码示例:

    var referenced = 1;
    var notReferenced = 2;  // Will this be enclosed?
    var func = function () {
        alert(referenced);
    }
(当我读到关于IE中通过使用Javascript闭包创建循环引用而导致内存泄漏的内容时,我想到了这个问题。)
注:我所说的“随附”是指MSDN所称的“已捕获”。()

我只能回答关于C的问题:

不,它不会被封闭,因为它是不需要的

我在本文中对“是否将其附上?”的解释如下:

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

免责声明:
这是一个实现细节。我的答案是正确的,对于MS针对您的具体情况实现的C#编译器。它可能与Mono编译器或更新版本的MS编译器不同。或者,正如乔恩的回答所显示的那样,对于一个更复杂的例子,情况可能会有所不同。

我只能从C方面回答

如果闭包没有实际捕获该变量,它将作为方法中的正常局部变量。如果它被捕获,它将被提升为合成类中的一个实例变量。(我假设当你谈论变量被“封闭”时,这就是你所说的。)

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

private Action Foo() {
    var expensive = ...;
    var cheap = ...;
    Action temp = () => Console.WriteLine(expensive);
    return () => Console.WriteLine(cheap);
}
在这里,返回的操作仍将保持“昂贵”引用的活动状态。不过,所有这些都是Microsoft C#4编译器的实现细节,将来可能会发生变化

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

我只能回答有关JavaScript的问题

这是特定于实现的。有些引擎包含所有变量。一些发动机进行优化,仅包含参考变量

我知道Chrome是优化的,只包含它关心的变量

进一步阅读:

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

通过在EcmaScript和主机对象之间进行循环引用,可以很好地读取IE8中仍然存在的内存泄漏:


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

这里有两个问题。在将来,当你有两个问题时,你可能会考虑发表两个单独的问题。 在Javascript中创建闭包时,闭包创建时作用域中的所有变量是否都“包含”在其中

您没有说明您正在谈论的“JavaScript”的许多版本中的哪一个。我假设您正在谈论ECMAScript 3语言的正确实现。如果你正在谈论“JavaScript”的其他版本,请说出你正在谈论的版本

ECMAScript 5更新了词法环境和“eval”如何工作的规则。自2001年以来,我没有成为技术委员会39的成员,我也没有及时了解ECMAScript 5规范的最新变化;如果您想在最近的ECMAScript 5规则的上下文中找到答案,请找到该规范的专家;我不是

在ECMAScript 3的上下文中,您的问题的答案是肯定的。ECMAScript 3规范在这一点上非常清楚,但您不需要查看规范就知道一定是这样的:

function f()
{
 var referenced = 1;
 var notReferenced = 2;
 var func = function () 
 {
   alert(referenced);
   alert(eval("notReferenced"));
 }
 return func;
}
f()();
如果未捕获“NotReference”,则“eval”如何正确工作

这在ECMAScript规范中都有解释,但我可以在这里简单总结一下

每个变量都与一个“变量对象”相关联,该对象的属性名称是变量的名称。函数f的变量对象与函数f的激活对象相同——也就是说,每次调用函数f时都会神奇地创建一个对象。变量对象有三个属性:“已引用”、“未引用”和“func”

有一个称为“全局对象”的变量对象,它表示任何函数之外的代码。它有一个属性“f”

每个执行上下文都有一个范围链,它是在尝试计算标识符时搜索其属性的对象列表

每个函数对象都有一个与之关联的范围链,它是创建函数时生效的范围链的副本

与“f”关联的范围链是全局对象

当执行进入“f”时,执行上下文的当前作用域链将激活对象“f”推送到它上面

当“f”创建分配给“func”的函数对象时,其关联的作用域链是执行上下文的当前作用域链的副本,即包含“f”激活的作用域链和全局对象

好了,现在所有的对象都设置正确了。f返回func,然后调用它。为该函数创建激活对象的。执行上下文的作用域链是从函数对象获取的——记住,它是“f”的激活对象加上全局对象——我们将当前激活对象推送到作用域链的副本上。因为我们现在执行的匿名函数没有
function f()
{
 var referenced = 1;
 var notReferenced = 2;
 var func = function () 
 {
   alert(referenced);
   alert(eval("notReferenced"));
 }
 return func;
}
f()();
function f()
{
 var func = function () 
 {
   alert(newlyCreated);
 }
 eval("var newlyCreated = 123;");
 return func;
}
f()();