var x = 1;
Func<int,int> f = y => x + y;
x = 2;
Console.WriteLine(f(1));
Run Code Online (Sandbox Code Playgroud)
根据http://www.cs.cornell.edu/~clarkson/courses/csci4223/2013sp/lec/lec12.pdf,输出为3.我认为它是2 .
Eri*_*ert 23
这个问题是我2013年5月20日博客的主题.谢谢你这个好问题!
你误解了"词法范围"意味着什么.让我们引用您链接到的文档:
函数的主体在定义函数时存在的旧动态环境中进行评估,而不是在调用函数时的当前环境.
这是你的代码:
int x = 1;
Func<int,int> f = y => x + y;
x = 2;
Console.WriteLine(f(1));
Run Code Online (Sandbox Code Playgroud)
现在,什么是"定义函数时存在的动态环境"?把"环境"想象成一个阶级.该类包含每个变量的可变字段.所以这和:
Environment e = new Environment();
e.x = 1;
Func<int,int> f = y => e.x + y;
e.x = 2;
Console.WriteLine(f(1));
Run Code Online (Sandbox Code Playgroud)
在f评估时,x查找创建f时存在的环境e.该环境的内容已更改,但绑定的环境f是相同的环境.(请注意,这实际上是C#编译器生成的代码!当您在lambda中使用局部变量时,编译器会生成一个特殊的"环境"类,并将本地的每个用法转换为字段的用法.)
让我举个例子说明如果C#是动态范围的,那么世界会是什么样子.考虑以下:
class P
{
static void M()
{
int x = 1;
Func<int, int> f = y => x + y;
x = 2;
N(f);
}
static void N(Func<int, int> g)
{
int x = 3;
Console.WriteLine(g(100));
}
}
Run Code Online (Sandbox Code Playgroud)
如果C#是动态范围的,那么这将打印"103",因为评估g评估f,并且在动态范围的语言中,评估f将查找x 当前环境中的值.在当前环境中,x是3.在f创建时存在的环境中,x是2.再次,该x环境中的值已经改变; 正如您的文档所指出的,环境是一个动态的环境.但哪个环境是相关的并没有改变.
如今大多数语言都不是动态范围的,但也有一些.PostScript,例如 - 在打印机上运行的语言 - 是动态范围的.
Ben*_*igt 22
有关词汇范围的微妙之处,PDF没有完全解释.它的例子实际上有两个不同的变量命名x,它不重新分配第一个的值x(实际上功能语言可能不允许变异).
C#是词法范围的 - 它查找xlambda的定义点,而不是在调用委托时.但是:x解析为变量而不是值,并且它在调用时读取变量的值.
这是一个更完整的例子:
int InvokeIt( Func<int, int> f )
{
int x = 2;
return f(1);
}
Func<int, int> DefineIt()
{
int x = 1;
Func<int, int> d = (y => x + y);
x = 3; // <-- the PDF never does this
return d;
}
Console.WriteLine(InvokeIt(DefineIt()));
Run Code Online (Sandbox Code Playgroud)
lambda绑定到内部存在的x 变量.定义点的值()无关紧要.该变量稍后设置为. DefineItx = 1x = 3
但它显然也不是动态范围,因为x = 2内部InvokeIt没有使用.