【问题标题】:Why can't a duplicate variable name be declared in a nested local scope?为什么不能在嵌套的局部范围内声明重复的变量名?
【发布时间】:2011-05-27 18:44:46
【问题描述】:

根据最近的question,我不明白所提供的答案。似乎你应该能够做这样的事情,因为它们的范围不重叠

static void Main()
{
  {
    int i;
  }
  int i;
}

此代码编译失败,出现以下错误:

不能在此范围内声明名为“i”的局部变量,因为它会给“i”赋予不同的含义,而“i”已在“子”范围内用于表示其他内容

【问题讨论】:

  • 我已将其重新标记为 C#,因为原始标签包含 .net 并且链接的 Q 建议使用 C#。另外,它是有效的 C 和 C++(如果你能克服狡猾的主要定义),但会在 C# 中产生错误。主要定义在 C# 中很好,但在 C 或 C++ 中没有。
  • @David Heffernan - 谢谢,这是一个错字
  • 相关(可能)重复:stackoverflow.com/q/7992332/945456

标签: c# .net


【解决方案1】:

我认为迄今为止的任何答案都没有相当从规范中得到关键线。

从第 8.5.1 节开始:

local-variable-declaration中声明的局部变量的范围是发生声明的块。在局部变量的 local-variable-declarator 之前的文本位置引用局部变量是错误的。在局部变量的范围内,声明另一个同名的局部变量或常量是编译时错误。

(强调我的。)

换句话说,“后来”变量的范围包括声明之前块的部分 - 即它包括包含“早期”变量的“内部”块。

您不能在声明之前的位置引用后面的变量 - 但它仍在范围内。

【讨论】:

  • 乔恩,你认为这真的有意义吗?如果有,为什么?
  • @DanielHilgarth:是的,我认为这是有道理的。它避免了混乱的代码。
  • 我同意如果在嵌套范围之前声明外部变量会造成混淆。但是在遵循 OP 给出的示例的代码中,您会在这里看到什么令人困惑的地方?只是想知道该规范背后的原因可能是什么。
  • @DanielHilgarth:在同一个块中将变量声明移到更早的位置通常是无操作的,对吧?所以导致编译错误会很奇怪。
【解决方案2】:

“局部变量或常量变量的范围延伸到当前块的末尾。你不能在当前块或任何嵌套块中声明另一个同名的局部变量。” C# 3.0 简而言之,http://www.amazon.com/3-0-Nutshell-Desktop-Reference-OReilly/dp/0596527578/

“块的局部变量声明空间包括任何嵌套块。因此,在嵌套块中,不可能声明与封闭块中的局部变量同名的局部变量。”变量作用域,MSDN,http://msdn.microsoft.com/en-us/library/aa691107%28v=vs.71%29.aspx

另一方面,这与 JavaScript 和 F# 范围规则完全相反。

【讨论】:

    【解决方案3】:

    来自C# language spec:

    块的局部变量声明空间包括任何嵌套块。因此,在嵌套块中,不可能声明与封闭块中的局部变量同名的局部变量。

    本质上,这是不允许的,因为在 C# 中,它们的作用域实际上是重叠的。

    编辑:澄清一下,C# 的作用域是在块级别解析的,而不是逐行解析的。因此,虽然您不能在代码中引用变量声明之前的变量是正确的,但它的范围一直延伸到块的开头也是正确的。

    【讨论】:

      【解决方案4】:

      从第一个版本开始,这就是 C# 中的规则。

      允许重叠范围只会导致混淆(程序员,而不是编译器)。

      所以这是故意禁止的。

      【讨论】:

      • 我认为 OP 的观点是范围 重叠。说不允许重叠范围有点离题。
      • 不仅禁止或目的,而且我认为有趣的是,在这种情况下错误消息与正常情况略有不同,即首先声明外部变量,其中消息是一个名为的局部变量'i' 不能在此范围内声明,因为它会给 'i' 赋予不同的含义,后者已在 '父级或当前' 范围内用于表示其他内容。
      • 见 Jon Skeets 的回答,块是范围,它们确实重叠。如果你愿意,你可以称它为“重叠块”。
      • @Gabe:不正确。在 O.P.s 示例代码中,尽管声明的位置(块中的最后一行),但第二个声明的范围是 整个 最外面的块(方法体)。内部块中的第一个声明是非法的,因为它继承了包含块的命名空间(ISO 中的“局部变量声明空间”)。结果,第一个声明在第二个声明上时是不允许的(即使对第二个声明的任何引用都是非法的:您会收到另一个编译器警告:re: using something before its declaration.
      【解决方案5】:

      对于 C#,ISO 23270(信息技术 - 编程 语言 — C#), §10.3 (Declarations) 说:

      每个 blockswitch-blockfor-statementforeach-statementusing-statement 为局部变量创建声明空间并 局部常量称为局部变量声明空间。名字是 通过local-variable-declarations引入这个声明空间 和局部常量声明

      如果块是实例的主体 构造函数、方法或运算符声明,或 get 或 set 访问器 索引器声明,在这种声明中声明的参数是 块的局部变量声明空间的成员。

      如果一个块是 泛型方法的主体,在此类声明中声明的类型参数 是块的局部变量声明空间的成员。

      这是一个错误 局部变量声明空间的两个成员具有相同的名称。 局部变量声明空间和嵌套的局部变量是错误的 变量声明空间包含同名元素。

      [注意: 因此,在嵌套块中,不可能声明本地块 与局部变量或常量同名的变量或常量 在一个封闭的块中。两个嵌套块可以包含 只要两个块都不包含另一个块,则具有相同名称的元素。 结束说明]

      所以

      public void foobar()
      {
        if ( foo() )
        {
           int i = 0 ;
           ...
        }
      
        if ( bar() )
        {
          int i = 0 ;
          ...
        }
      
        return ;
      }
      

      是合法的,但是

      public void foobar()
      {
        int i = 0 ;
      
        if ( foo() )
        {
           int i = 0 ;
           ...
        }
      
        ...
      
        return ;
      }
      

      不合法。就个人而言,我发现限制相当烦人。我可以看到发出关于范围重叠的编译器警告,但是编译错误?太多的腰带和吊带,恕我直言。不过,我可以看到编译器选项和/或 pragma 的优点(可能是 -pedantic/-practical#pragma pedantic#pragma practicalB^))。

      【讨论】:

        【解决方案6】:

        这不是重叠范围的问题。在 C# 中,一个简单的名称在声明它的块中不能表示多于一件事。在您的示例中,名称 i 表示同一外部块中的两个不同事物。

        换句话说,您应该能够将变量声明移动到声明它的块中的任何位置,而不会导致范围重叠。由于将您的示例更改为:

        static void Main()
        {
            int i;
            {
                int i;
            }
        }
        

        会导致不同i变量的范围重叠,你的例子是非法的。

        【讨论】:

        • 不完全是,i 可能出现在另一个嵌套块中,并在其中表示其他含义。
        • @Henk:看起来我的措辞可能含糊不清。现在清楚了吗?
        • 是的。我稍微忽略了排序。
        【解决方案7】:

        我刚刚在 GCC 中将它编译为 C 和 C++。我没有收到任何错误消息,所以它似乎是有效的语法。

        您的问题被标记为 .net 和 c。这应该标记为 c# 吗?该语言的规则可能与 C 不同。

        【讨论】:

          【解决方案8】:

          在 C 语言中,您需要将所有变量声明放在块的开头。它们需要在此块中的任何其他语句之前的开头 { 之后直接出现。

          所以你可以做的是让它编译是这样的:

          static void Main()
          {
            {
              int i;
            }
            {
              int i;
            }
          }
          

          【讨论】:

          • 问题已被标记为 [C#]。
          • 从那以后就不是这样了,什么,C89?
          • 实际上在现代版本的 C 中不再如此。OP 的代码在 Comeau online 中编译为 C99。
          • 还有,static void Main() 是从什么时候开始启动 C 程序的?!!
          • 我被 C 标记误导了。但在 ANSI C (C89/C90) 中确实如此,尽管在 C99 正式允许之前,许多编译器都有对其进行编译的扩展。默认情况下,一些嵌入式编译器,即 Keil 和 MSVC 仍然不允许在语句后声明。我的 C# 知识非常有限,但似乎我的代码可以按照 Teoman Soygul 引用的规范进行编译。
          【解决方案9】:

          这是您来自MSDN .NET Documentation的回答:

          ...块的局部变量声明空间包括任何嵌套块。因此,在嵌套块中,不可能声明与封闭块中的局部变量同名的局部变量。

          【讨论】:

            猜你喜欢
            • 2018-03-15
            • 2019-04-07
            • 1970-01-01
            • 2018-01-06
            • 1970-01-01
            • 2015-09-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多