为什么我可以声明一个与父作用域中的变量同名的子变量?

Why can I declare a child variable with the same name as a variable in the parent scope?

我最近写了一些代码,无意中重复使用了一个变量名作为函数中声明的一个动作的参数,而这个函数已经有一个同名变量。例如:

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

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

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

Why does this code work? I'm using C# 8 with Visual Studio 2019.

您已经回答了自己的问题!这是因为您使用的是 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# 8,C# 团队决定整个规则造成的混乱多于它所防止的混乱,因此该规则从语言中退出。 (感谢 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/

在第三部分的末尾,我注意到此功能与 "Color Color" 功能之间也存在交互作用——即允许的功能:

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

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

我从来没有写过那篇描述这两个规则之间所有奇怪的相互作用的文章,现在写就有点毫无意义了!