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

ste*_*r42 26 c# lambda scope variable-names c#-8.0

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

var x = 1;
Action<int> myAction = (x) => { Console.WriteLine(x); };
Run Code Online (Sandbox Code Playgroud)

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

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

Eri*_*ert 29

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

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

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

此规则的目的是防止您在示例中谈论的那种情况,在这种情况下,很容易混淆本地的含义。特别是,此规则旨在防止混淆,例如:

class C 
{
  int x;
  void M()
  {
    x = 123;
    if (whatever)
    {
      int x = 356;
      ...
Run Code Online (Sandbox Code Playgroud)

现在我们有一种情况,在 body 里面,M,x意味着两者this.x和本地x

尽管本意是善意的,但这条规则存在许多问题:

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

我在 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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