为什么我可以声明一个与父作用域中的变量同名的子变量? [英] Why can I declare a child variable with the same name as a variable in the parent scope?

查看:21
本文介绍了为什么我可以声明一个与父作用域中的变量同名的子变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wrote some code recently where I unintentionally reused a variable name as a parameter of an action declared within a function that already has a variable of the same name. For example:

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

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

When I spotted the duplication, I was surprised to see that the code compiled and ran perfectly, which is not behavior I would expect based on what I know about scope in C#. Some quick Googling turned up SO questions that complain that similar code does produce an error, such as Lambda Scope Clarification. (I pasted that sample code into my IDE to see if it would run, just to make sure; it runs perfectly.) Additionally, when I enter the Rename dialog in Visual Studio, the first x is highlighted as a name conflict.

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

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

推荐答案

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

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

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

You've answered your own question! It's because you're using C# 8.

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

The rule from C# 1 through 7 was: a simple name cannot be used to mean two different things in the same local scope. (The actual rule was slightly more complex than that but describing how is tedious; see the C# specification for details.)

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

The intention of this rule was to prevent the sort of situation that you're talking about in your example, where it becomes very easy to be confused about the meaning of the local. In particular, this rule was designed to prevent confusions like:

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

现在我们有这样一种情况,在 M 的主体内部,x 意味着 this.x 和本地 x.

And now we have a situation where inside the body of M, x means both this.x and the local x.

虽然本意是好的,但这条规则存在许多问题:

Though well-intentioned, there were a number of problems with this rule:

  • 它没有按照规范实现.在某些情况下,一个简单的名称可以同时用作类型和属性,但它们并不总是被标记为错误,因为错误检测逻辑存在缺陷.(见下文)
  • 错误消息措辞混乱,报告不一致.这种情况有多种不同的错误消息.他们不一致地确定了罪犯;也就是说,有时会调用内部用法,有时会调用外部,有时只是令人困惑.
  • It was not implemented to spec. There were situations where a simple name could be used as, say, both a type and a property, but these were not always flagged as errors because the error detection logic was flawed. (See below)
  • The error messages were confusingly worded, and inconsistently reported. There were multiple different error messages for this situation. They inconsistently identified the offender; that is, sometimes the inner usage would be called out, sometimes the outer, and sometimes it was just confusing.

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

I made an effort in the Roslyn rewrite to sort this out; I added some new error messages, and made the old ones consistent regarding where the error was reported. However, this effort was too little, too late.

C# 团队决定在 C# 8 中整条规则造成的混乱多于防止的混乱,并且该规则从语言中退休.(感谢 Jonathon Chase 确定何时退休.)

The C# team decided for C# 8 that the whole rule was causing more confusion than it was preventing, and the rule was retired from the language. (Thanks Jonathon Chase for determining when the retirement happened.)

如果您有兴趣了解这个问题的历史以及我如何尝试修复它,请参阅我写的这些文章:

If you are interested to learn the history of this problem and how I attempted to fix it, see these articles I wrote about it:

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/

在第三部分的结尾,我注意到该功能与颜色颜色"功能之间也存在交互作用——也就是说,该功能允许:

At the end of part three I noted that there was also an interaction between this feature and the "Color Color" feature -- that is, the feature that allows:

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

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

Here we have used the simple name Color to refer to both this.Color and the enumerated type Color; according to a strict reading of the specification this should be an error, but in this case the spec was wrong and the intention was to allow it, as this code is unambiguous and it would be vexing to make the developer change it.

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

I never did write that article describing all the weird interactions between these two rules, and it would be a bit pointless to do so now!

这篇关于为什么我可以声明一个与父作用域中的变量同名的子变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆