当我声明与lambda中的变量同名的变量时,C#会疯狂

Rub*_*bys 2 c# lambda

我有以下代码(生成给定a,b和c的二次函数)
Func<double, double, double, Func<double, double>> funcGenerator = (a, b, c) => f => f * f * a + b * f + c;
到目前为止,很可爱.
但是,如果我尝试声明一个名为a,b,c或f的变量,visual studio弹出一个"A local variable named 'f' could not be declared at this scope because it would give a different meaning to 'f' which is used in a child scope."
基本上,这会失败,我不知道为什么,因为一个子范围甚至没有任何意义.

Func<double, double, double, Func<double, double>> funcGenerator =
    (a, b, c) => f => f * f * a + b * f + c;  
var f = 3; // Fails  
var d = 3; // Fine
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?

Aar*_*ght 12

我认为您误解的是,声明的顺序与C#编译器相关的范围规则无关紧要.

这个:

Func<double, double, double, Func<double, double>> funcGenerator =
    (a, b, c) => f => f * f * a + b * f + c;  
var f = 3;
var d = 3;
Run Code Online (Sandbox Code Playgroud)

与此完全相同:

var f = 3;
Func<double, double, double, Func<double, double>> funcGenerator =
    (a, b, c) => f => f * f * a + b * f + c;  
var d = 3;
Run Code Online (Sandbox Code Playgroud)

范围不是对订单敏感的.你有一个名为的局部变量f,并且你试图声明f在lambda中命名的另一个变量.根据C#规范,这是非法的.

具体来说,它会与lambdas进行变量捕获的能力发生冲突.例如,此代码是合法的:

int x = 3;
Func<int> func = () => x + 1;
Run Code Online (Sandbox Code Playgroud)

这是完全合法的,执行func()将返回4.这就是为什么你不能x在lambda中声明另一个变量的原因- 因为lambda实际上需要能够捕获外部x.

只需更改其中一个f变量的名称即可.


Mat*_*hen 8

它意味着它所说的.您不允许在lambda范围和包含lambda的范围中使用相同的变量名.子范围是lambda的范围.

  • Rubys,那里*是*儿童范围.范围在C#中不需要`{}`.lambda定义在文本上位于`var f`之上并不重要. (3认同)

Eri*_*ert 7

确切地确定你违反了哪个C#规则可能会非常棘手.作为一项公共服务,我编写了这本方便的指南,解释了一些更容易混淆的范围规则之间的差异:

http://ericlippert.com/tag/simple-names/

(从底部开始;这些是按时间顺序排列的.)

另外,你对"范围"这个概念似乎有点不清楚 - 这并不奇怪,因为在大多数书中,这个词几乎用来表示作者想要的东西.在C#中,我们仔细地将"范围"定义为"程序文本的区域,其中特定实体可以通过其不合格的名称来引用".例如,在

namespace A 
{
  public class B 
  {
      private int c;
      protected int d;
      public void E(int f)
      {
         int g = f;
         Func<int, int> h = i => g * i;
      }
  }
  public class K : B { }
}
Run Code Online (Sandbox Code Playgroud)

A的范围无处不在.B和K的范围在A的声明中无处不在.c的范围在B内无处不在.d和E的范围是B,K的范围以及从B或K派生的任何类.范围f和g和h是E的主体.i的范围是lambda的主体.

请注意,范围和可访问性域之间存在差异.B,K和E 随处可访问,但仅限于特定位置的范围.

另请注意,整个块的h范围.在声明之前使用它是不合法的,但它声明之前就在范围之内. h