Pac*_*ier 4 javascript c# vb.net scope scope-chain
我已经读过了由于范围链如何在javascript中工作,如果我们希望引用未在F范围内声明的函数F中的变量V,那么声明一个是有益的(在性能方面是肯定的) F中的局部变量V2引用V,然后访问V引用的对象到V2.
我想知道这个概念是否适用于C#和VB中的闭包(通过lambdas访问函数中的局部变量)
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
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果答案不是过早的优化,那么我更愿意是所有邪恶的根源
简答:不..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
}
Run Code Online (Sandbox Code Playgroud)
在该CaptureArgs方法中,a并且b存在在堆栈中.直观地说,如果我们在一个匿名函数引用变量,返回的功能和出栈帧应该删除a,并b从内存.(这被称为向上的funargs问题).
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);
}
}
Run Code Online (Sandbox Code Playgroud)
编译器创建并返回一个新实例<>c__DisplayClass1,从其初始化它a和b字段a并b传入CaptureArgs方法(这有效地从堆栈复制a并传递到b堆中存在的字段),并将其返回给调用者.通话f()真的是一个电话<>c__DisplayClass1.<CaptureArgs>b__0().
由于a和b引用的<CaptureArgs>b__0是vanilla字段,它们可以由委托直接引用,它们不需要任何特殊类型的范围链规则.
如果我理解正确,JavaScript的问题如下:当您访问(深度)嵌套范围中的变量时,运行时需要遍历所有父范围以定位变量.
C#或Visual Basic中的Lambda不会遇到此问题.
在VB或C#中,编译器确切地知道您在lambda函数中引用哪个变量,因此它可以创建对该变量的直接引用.唯一的区别是捕获的变量(从嵌套范围访问的变量)必须从局部变量转换为字段(在某些对象中,也称为闭包).
添加一个例子 - 说你写这样的东西(疯狂):
Func<Func<int>> Foo() {
int x = 10;
return () => {
x++;
return () => x;
}
}
Run Code Online (Sandbox Code Playgroud)
这有点傻,但它演示了嵌套的范围.变量在一个作用域中声明,在嵌套作用域中设置并在更深的范围内读取.编译器将生成如下内容:
class Closure {
public Closure(int x) { this.x = x; }
public int x;
public Func<int> Nested1() {
x++;
return Func<int>(Nested2);
}
public int Nested2() { return x; }
}
Func<Func<int>> Foo() {
var clo = new Closure(10);
return Func<Func<int>>(clo.Nested1);
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的 - 没有走过一系列范围.每次访问变量时x,运行时都会直接访问内存中的某个位置(在堆上分配,而不是堆栈).
| 归档时间: |
|
| 查看次数: |
222 次 |
| 最近记录: |