我已经读到,由于作用域链在javascript中是如何工作的,如果我们希望引用未在F范围内声明的函数F中的变量V,则对(在性能方面)是有益的在F中声明一个引用V的局部变量V2,然后通过V2访问V引用的对象。
我想知道这个概念是否适用于C#和VB中的闭包(通过lambda访问函数中的局部变量)
Public Shared Function Example()
Dim a = 1
Dim b = New Object
Return Sub()
'when we use the variables a and b from here does it have to "go up the scope chain"
End Sub
End Function
顺便说一句,我想如果答案不是,那过早的优化是所有邪恶的根源
最佳答案
简短的回答:不。 .NET不需要遍历作用域链来查找变量。
长答案:
从以下示例开始:
static Func<string> CaptureArgs(int a, int b)
{
return () => String.Format("a = {0}, b = {1}", a, b);
}
static void Main(string[] args)
{
Func<string> f = CaptureArgs(5, 10);
Console.WriteLine("f(): {0}", f());
// prints f(): a = 5, b = 10
}
在
CaptureArgs
方法中,a
和b
存在于堆栈中。直观地,如果我们在匿名函数中引用变量,则返回该函数并弹出堆栈框架应从内存中删除a
和b
。 (这称为upward funargs problem)。C#不会遭受向上的funargs问题,因为在后台,匿名函数只是编译器生成的类上的漂亮语法糖。上面的C#代码变成:
private sealed class <>c__DisplayClass1
{
// Fields
public int a;
public int b;
// Methods
public string <CaptureArgs>b__0()
{
return string.Format("a = {0}, b = {1}", this.a, this.b);
}
}
编译器创建并返回一个新的
<>c__DisplayClass1
实例,从传递给a
方法的b
和a
初始化其b
和CaptureArgs
字段(这实际上将a
和b
从堆栈复制到堆中现有的字段中),并将其返回给 call 者。调用f()
实际上是对<>c__DisplayClass1.<CaptureArgs>b__0()
的调用。由于
a
中引用的b
和<CaptureArgs>b__0
是普通字段,因此委托(delegate)可以直接引用它们,因此它们不需要任何特殊的作用域链接规则。