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

时间:2012-05-09 14:16:34

标签: c# .net linq closures anonymous-methods

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

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

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

  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,但它正在成功编译而没有任何警告/错误。

问题,评估和评估3都不应该编译,或者两者都应该编译。

2 个答案:

答案 0 :(得分:7)

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

int i;

...从开始到结束在整个封闭范围内生效 - 不仅仅是从声明它的位置开始。在.Net中,局部变量的声明只是编译器为整个范围保留该名称和本地的指令。这意味着一旦宣布,它已经为之前和之后的所有行保留。

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

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

如果你相应地写了你的方法,你会发现编译器错误发生在lambda声明上 - 这是完全合理的。值得庆幸的是,从人类的角度来看,C#编译器非常聪明,认识到代码的排序对 us 很重要,它实际上将编译器错误分配给第二个或后续声明中的任何源代码行;因此,为什么在您的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共享父函数范围,但它们也有自己的,并且它们在那里声明了它们的i - 这就是为什么它们不要互相干扰(实际上,它们是兄弟范围)。

顺便提及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实际上每种范围都有不同的类型。就像我说的那样 - 我再也没有这样做过,而且有问题的代码已不再存在了:)

答案 1 :(得分:1)

来自the spec

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都不在其中另一个的范围,所以没有错误。

您的段落“基于......两者的逻辑,是功能块的本地...”是不正确的 - 第一个i不是功能块的本地,而是lambda。