linq匿名方法中的局部变量范围(闭包)

Linq Query中声明的局部变量的范围是什么。

我正在编写以下代码

static void Evaluate() { var listNumbers = Enumerable.Range(1, 10).Select(i => i); int i = 10; } 

编译器标记错误在行int i = 10,说明

 A local variable named 'i' cannot be declared in this scope because it would give a different meaning to 'i', which is already used in a 'child' scope to denote something else 

我无法理解为什么会出现这个错误。

我的理解是, i将在第一行之后(在foreach循环中)超出范围。 所以i可以再次宣布。

实际行为是i在第一行(在foreach循环中)之后无法访问,这是正确的。 但i不能再宣布了。 这看起来很奇怪。

编辑这是基于安德拉斯的回应的以下问题。 答案非常好,但引起了进一步的质疑。

  static void Evaluate3() { var listNumbers = Enumerable.Range(1, 10).Select(i => i); var listNumbers1 = Enumerable.Range(1, 10).Select(i => i); } 

基于函数的逻辑评估.Select(i => i)和int i = 10,两者都是函数块的局部因而是复杂性错误。

函数Evaluate3不应该编译,因为方法块中有两个i,但它正在成功编译而没有任何警告/错误。

问题,Evaluate和Evaluate3都不应该编译,或者两者都应该编译。

这里要注意的关键事实是声明:

 int i; 

从头到尾贯穿整个封闭范围 – 不仅仅是从声明它的位置开始生效。 在.Net中,局部变量的声明只是编译器为整个范围保留该名称和本地的指令。 这意味着一旦声明,它就已经为前后的所有线路保留了。

实际上,这意味着您应该将Evaluate实际读作:

 static void Evaluate() { int i; var listNumbers = Enumerable.Range(1, 10).Select(i => i); i = 10; } 

如果你相应地编写了你的​​方法,你会发现编译器错误发生在lambda声明上 – 这是完全合理的。 值得庆幸的是,从人的角度来看,C#编译器非常聪明,认识到代码的排序对我们很重要,它实际上将编译器错误分配给第二个或后续声明中的任何源代码行; 因此,为什么在你的Evaluate版本中它发生在int i = 10; 。 有了函数本地i的实际生命周期的知识,编译器是正确的:使用i 与早期在lambda中使用i冲突。

您可以使用显式作用域来避免这种情况:

 static void Evaluate() { var listNumbers = Enumerable.Range(1, 10).Select(i => i); { int i = 10; } } 

Evaluate3的情况下,你只需要注意,虽然两个lambdas共享父函数范围,但它们也有自己的,并且在那里它们的声明被声明 – 这就是为什么它们不互相干扰(它们是,实际上,兄弟范围)。

顺便说一下, EvaluateEvaluate3最终可以简化为:

 static void Evaluate() { { int i; } int i; //<-- error } static void Evaluate3() { { int i; } { int i; } //fine here - both i's are in different scopes. } 

实际上,这是我在此之前使用显式范围的第二种情况 - 也就是说,在同一函数中的不同范围内,其中i实际上在每个范围内都有不同的类型。 就像我说的 - 我再也没有这样做过,而且有问题的代码已不再存在了:)

从规格 :

 The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the local-variable-declarator of the local variable. 

你的lambda i => i在本地变量int i = 10的范围内,即使它是事先声明的。 由于你在声明它之前使用了i ,而不是抛出一个错误,编译器有助于指出你已经使用过i来引用别的东西而且这个声明会改变它。

编辑:更新后:

在第一种情况下,你的第一个i被包含在lambda中,但你的第二个i包含整个Evaluate方法, 包括lambda – 因此你得到了错误。

在第二种情况下,你的第一个i被包含在它的lambda中,你的第二个i被包含在它的 lambda中 – i都不在另一个范围内,所以没有错误。

您的段落“基于……我的两个逻辑,是function块的本地…”是不正确的 – 第一个i不是function块的本地,而是lambda。