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


23

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

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

当我发现重复项时,我很惊讶地看到代码可以完美地编译和运行,这并不是基于我对C#范围的了解而期望的行为。一些快速的Google搜索出现SO问题,它们抱怨类似的代码确实会产生错误,例如Lambda Scope Clarification。(我将示例代码粘贴到IDE中,以确保它可以运行;只是为了确保它可以正常运行。)此外,当我在Visual Studio中进入“重命名”对话框时,第一个x突出显示为名称冲突。

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


1
lambda移至由编译器生成的类上的方法,因此该x方法的整个参数都移出了作用域。有关示例,请参见Sharplab
Lasse V. Karlsen

6
在这里可能值得注意的是,在针对C#7.3时,它将无法编译,因此这似乎是C#8所独有的。
Jonathon Chase

链接问题中的代码在Sharplab中也可以很好地编译。这可能是最近的更改。
Lasse V. Karlsen

2
发现了一个欺骗(无答案):stackoverflow.com/questions/58639477/...
bolov

Answers:


26

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

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

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

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

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

现在我们的体内所处的环境Mx手段都this.x和当地x

尽管有很好的意图,但是此规则存在许多问题:

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

我在罗斯林重写中进行了努力以解决此问题;我添加了一些新的错误消息,并使旧消息与报告错误的位置保持一致。但是,这种努力太少,为时已晚。

C#团队为C#8决定,整个规则引起的混乱比它要阻止的更多,并且该规则已从语言中淘汰。(感谢乔纳森·蔡斯确定退休时间。)

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

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:谢谢!
埃里克·利珀特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.