【问题标题】:Why can I declare a child variable with the same name as a variable in the parent scope?为什么我可以声明一个与父作用域中的变量同名的子变量?
【发布时间】:2020-01-29 17:58:41
【问题描述】:

我最近编写了一些代码,无意中重用了一个变量名作为在已经具有同名变量的函数中声明的操作的参数。例如:

var x = 1;
Action<int> myAction = (x) => { Console.WriteLine(x); };

当我发现重复时,我惊讶地发现代码编译和运行完美,根据我对 C# 范围的了解,这不是我所期望的行为。一些快速的谷歌搜索出现了抱怨类似代码确实产生错误的 SO 问题,例如Lambda Scope Clarification。 (我将该示例代码粘贴到我的 IDE 中以查看它是否会运行,以确保它运行完美。)此外,当我进入 Visual Studio 中的重命名对话框时,第一个 x 突出显示为名称冲突。

为什么这段代码有效?我在 Visual Studio 2019 中使用 C# 8。

【问题讨论】:

  • lambda 被移动到由编译器生成的类的方法中,因此该方法的整个 x 参数被移出范围。有关示例,请参见 sharplab
  • 这里可能值得注意的是,当以 C# 7.3 为目标时,它不会编译,因此这似乎是 C#8 独有的。
  • 链接问题中的代码在sharplab 中也可以正常编译。这可能是最近的变化。
  • 发现了一个骗子(没有答案):stackoverflow.com/questions/58639477/…

标签: c# lambda scope c#-8.0 variable-names


【解决方案1】:

为什么这段代码有效?我在 Visual Studio 2019 中使用 C# 8。

您已经回答了自己的问题!这是因为您使用的是 C# 8。

从 C# 1 到 7 的规则是:一个简单的名称不能用于表示同一局部范围内的两个不同事物。 (实际的规则比这稍微复杂一些,但描述起来很乏味;有关详细信息,请参阅 C# 规范。)

此规则的目的是防止出现您在示例中讨论的那种情况,在这种情况下,很容易混淆 local 的含义。特别是,该规则旨在防止以下混淆:

class C 
{
  int x;
  void M()
  {
    x = 123;
    if (whatever)
    {
      int x = 356;
      ...

现在我们有一种情况,在 M 的主体内部,x 表示 this.x 和本地 x

虽然出于善意,但这条规则存在许多问题:

  • 未按规范实施。在某些情况下,一个简单的名称可以同时用作类型和属性,但由于错误检测逻辑存在缺陷,这些并不总是被标记为错误。 (见下文)
  • 错误消息措辞混乱,报告不一致。这种情况有多个不同的错误消息。他们不一致地确定了罪犯;也就是说,有时会调用 inner 用法,有时会调用 outer,有时它只是令人困惑。

我在 Roslyn 重写中努力解决这个问题;我添加了一些新的错误消息,并使旧错误消息与报告错误的位置保持一致。然而,这种努力太少,太晚了。

C# 团队决定,对于 C# 8,整个规则造成的混乱比它所防止的要多,因此该规则已从该语言中退出。 (感谢 Jonathon Chase 确定退休发生的时间。)

如果您有兴趣了解此问题的历史以及我如何尝试解决此问题,请参阅我撰写的有关此问题的以下文章:

https://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/

https://ericlippert.com/2009/11/05/simple-names-are-not-so-simple-part-two/

https://ericlippert.com/2014/09/25/confusing-errors-for-a-confusing-feature-part-one/

https://ericlippert.com/2014/09/29/confusing-errors-for-a-confusing-feature-part-two/

https://ericlippert.com/2014/10/03/confusing-errors-for-a-confusing-feature-part-three/

在第三部分结束时,我注意到此功能与“颜色颜色”功能之间也存在交互——即允许:

class C
{
  Color Color { get; set; }
  void M()
  {
    Color = Color.Red;
  }
}

这里我们使用了简单的名称Color来指代this.Color和枚举类型Color;根据对规范的严格阅读,这应该是一个错误,但在这种情况下,规范是错误的,并且意图是允许它,因为这段代码是明确的,让开发人员更改它会很麻烦。

我从来没有写过描述这两个规则之间所有奇怪交互的文章,现在这样做有点毫无意义!

【讨论】:

  • 问题中的代码无法为 C# 6、7、7.1、7.2 和 7.3 编译,给出“CS0136:不能在此范围内声明名为“x”的本地或参数,因为该名称……”。似乎该规则在 C# 8 之前仍然有效。
  • @JonathonChase:谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-11
  • 1970-01-01
  • 2020-05-16
  • 1970-01-01
相关资源
最近更新 更多