代码之家  ›  专栏  ›  技术社区  ›  Rubys

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

  •  2
  • Rubys  · 技术社区  · 15 年前

    我有以下代码(生成给定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
    

    怎么回事?

    5 回复  |  直到 10 年前
        1
  •  12
  •   Aaronaught    15 年前

    我认为您误解的是,声明的顺序对于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;
    

    与此完全相同:

    var f = 3;
    Func<double, double, double, Func<double, double>> funcGenerator =
        (a, b, c) => f => f * f * a + b * f + c;  
    var d = 3;
    

    作用域不区分顺序。有一个名为 f ,您正试图声明另一个名为 f 在lambda里面。根据C规范,这是非法的。

    具体来说,这将与lambdas捕获变量的能力相冲突。例如,本准则是合法的:

    int x = 3;
    Func<int> func = () => x + 1;
    

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

    只需更改其中一个 f 变量。

        2
  •  8
  •   Matthew Flaschen    15 年前

    它意味着它所说的。不允许在lambda作用域和包含lambda的作用域中使用相同的变量名。子作用域是lambda的作用域。

        3
  •  7
  •   Eric Lippert    10 年前

    很难弄清楚你到底违反了哪条规则。作为一名公共服务人员,我编写了这本方便的指南,解释了一些更容易混淆的范围界定规则之间的差异:

    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 { }
    }
    

    A的范围无处不在。b和k的作用域在a的声明中无处不在。c的作用域在b的任何地方。d和e的作用域是b、k和从b或k派生的任何类的内容。f和g和h的作用域是e的主体。i的作用域是lambda的主体。

    请注意,作用域和可访问性域之间存在差异。B,K和E是 可接近的 到处都是,但是 在范围 在特定的地点。

    还要注意 h 在范围内 自始至终 街区。不合法 使用 H 在宣布之前,但是 在范围内 在宣布之前。

        4
  •  2
  •   Daniel Earwicker    15 年前

    lambda的参数和来自封闭作用域的变量位于同一“命名空间”(在松散名称绑定意义上,而不是语言功能意义上)的原因是lambda可以关闭(引用)来自封闭作用域的变量。lambda的参数必须与lambda可以看到的变量区分开来:

    int x;
    Action a = () => { x = 3; };
    

    没关系,它给外部分配了3个 x .

    int x;
    Action<int> a = x => { x = 3; };
    

    那不好-那 X 我们给3分吗?

    在您的示例中,唯一的区别是声明的顺序。这会产生一个错误

    Action a = () => { x = 3; };
    int x = 2;
    

    编译器会说你不能引用 X 在宣布之前。如果让lambda取一个同名的参数,我们将得到大致的示例:

    Action<int> a = x => { x = 3; };
    int x;
    

    如果这样做了,编译器基本上会遵循这样一条规则:“尝试对代码进行各种可能的解释,无论哪种解释没有错误,都假设它是预期的含义”。C采取稍微安全一点的方法,并希望你是具体和明确的,而不是依赖这样的规则来“挑选赢家”。

        5
  •  0
  •   Henri    15 年前

    变量在函数范围内声明。由于lambda被编译/重写为同一函数中的某个代码,因此它有某种意义。我认为在for循环中,将变量定义为for循环的“第一个参数”是唯一的例外。

    尽管我可以想象,如果您可以重用变量名,它会很方便。