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

Til*_*lak 6 .net c# linq closures anonymous-methods

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

我正在编写以下代码

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

编译器标记的错误在行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 
Run Code Online (Sandbox Code Playgroud)

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

我的理解是,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);
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

And*_*tan 8

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

int i;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

您可以使用显式范围来避免这种情况:

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

Evaluate3你只是注意到两个lambdas共享父函数范围的情况下,它们也有自己的,并且它们在那里声明了它们i- 这就是为什么它们不相互干扰(它们实际上是,兄弟范围).

顺便提一下Evaluate,Evaluate3最终可以简化为:

static void Evaluate()
{
  { 
    int i;
  }
  int i; //<-- error
}

static void Evaluate3()
{
   { 
     int i;
   }
   { 
     int i;
   }
   //fine here - both i's are in different scopes.
}
Run Code Online (Sandbox Code Playgroud)

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