我已经读到,由于作用域链在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方法中,ab存在于堆栈中。直观地,如果我们在匿名函数中引用变量,则返回该函数并弹出堆栈框架应从内存中删除ab。 (这称为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方法的ba初始化其bCaptureArgs字段(这实际上将ab从堆栈复制到堆中现有的字段中),并将其返回给 call 者。调用f()实际上是对<>c__DisplayClass1.<CaptureArgs>b__0()的调用。

由于a中引用的b<CaptureArgs>b__0是普通字段,因此委托(delegate)可以直接引用它们,因此它们不需要任何特殊的作用域链接规则。

08-18 16:09