关于.net:为什么C#不允许我在不同的范围内使用相同的变量名?

Why doesn't C# allow me to use the same variable name in different scopes?

例如:

1
2
3
4
5
6
if ( this.IsValid )
{
    Matrix matrix = new Matrix();
}

Matrix matrix = new Matrix();

编译器警告我说:

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

这些变量不是在不同的范围内吗,所以我无论如何都不能从if语句外部访问第一个matrix


更新:以下2011年的答案对于早期版本的C是正确的。在较新版本中,描述答案的规则已从C中删除。设计团队认为,这条规则在开发人员中引起了更多的混乱,导致了像这样的问题,而不是错误程序所能保证的,即使在我大大改进了错误消息以更清楚地诊断问题之后。

到目前为止给出的答案非常令人困惑。正确的问题分析从读取错误消息开始。错误消息告诉您实际错误:

"A local variable named 'matrix' cannot be declared in this scope because it would give a different meaning to 'matrix', which is already used in a 'child' scope to denote something else.

仔细阅读。它精确地告诉你违反了C的哪条规则,也就是说,在同一范围内,不允许你使用相同的名称来引用两个不同的事物。(实际上,错误消息是有点错误的;它应该说"局部变量声明空间",在这里说"范围",但这很冗长。)

该规则记录在C 4.0规范第7.6.2.1节:简单名称、块中不变含义中。

(在重叠的声明空间中有两个同名的局部变量也是非法的。编译器也可能会报告该错误,但在本例中它会报告更一般的错误。)

Aren't these variables in different scopes, so I wouldn't be able to access the first matrix from outside the if statement anyway?

对。这句话是真的,但无关紧要。这里的错误是,在同一个局部变量声明空间中,使用了相同的简单名称来引用两个不同的内容。

考虑这种情况:

1
2
3
4
5
6
7
8
9
10
11
12
class C
{
    int x;
    void M()
    {
        x = 10; // means"this.x"
        for(whatever)
        {
            int x = whatever;
        }
    }
 }

同样的交易。这里的错误是,在外部声明空间中使用简单名称"x"来引用.x,并在内部声明空间中使用"局部变量"。在同一个声明空间中使用相同的简单名称来引用两个不同的事物——记住,内部声明空间是外部声明的一部分——既混乱又危险,因此是非法的。

由于明显的原因,它是令人困惑的;人们有一个合理的期望,即在其首次使用的声明空间中,名称在任何地方都将意味着相同的事物。这是危险的,因为小代码编辑很容易改变含义:

1
2
3
4
5
6
7
8
9
10
11
12
13
class C
{
    int x;
    void M()
    {
        int x;
        x = 10; // no longer means"this.x"
        for(whatever)
        {
            x = whatever;
        }
    }
 }

如果最初使用简单名称的声明空间没有重叠,那么简单名称引用不同的内容是合法的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class C
{
    int x;
    void M()
    {
        {
            x = 10; // means"this.x"
        }
        for(whatever)
        {
            int x = whatever; // Legal; now the
        }
    }
 }

更多信息和关于油炸食品的有趣故事,请参见

http://blogs.msdn.com/b/ericlippert/archive/tags/simple+名称/


我相信这是为了避免晦涩难懂的错误或难以阅读的代码。

使用方法范围和子范围之间的变量名可以导致非常难读取的代码,因为变量类型和更坏的含义可以改变,而对读者的唯一提示将是变量之前的类型声明关键字。

但是,我也可以告诉您,C编译器为方法生成的IL将把所有变量声明都粘贴在顶部,所以这个决策驱动程序可能是为了简化编译器的变量解析树。

事实上,您可以在msdn上找到:

The scope of a name is the region of
program text within which it is
possible to refer to the entity
declared by the name without
qualification of the name. Scopes can
be nested, and an inner scope may
redeclare the meaning of a name from
an outer scope. (This does not,
however, remove the restriction
imposed by Section 3.3 that within a
nested block it is not possible to
declare a local variable with the same
name as a local variable in an
enclosing block.) The name from the
outer scope is then said to be hidden
in the region of program text covered
by the inner scope, and access to the
outer name is only possible by
qualifying the name.

强调添加。

从第3.3节:

Each block or switch-block creates a
different declaration space for local
variables and constants. Names are
introduced into this declaration space
through local-variable-declarations
and local-constant-declarations. If a
block is the body of an instance
constructor, method, or operator
declaration, or a get or set accessor
for an indexer declaration, the
parameters declared in such a
declaration are members of the block's
local variable declaration space. The
local variable declaration space of a
block includes any nested blocks.
Thus, within a nested block it is not
possible to declare a local variable
with the same name as a local variable
in an enclosing block.

强调添加。

所以,问题是,虽然范围不同,但变量空间是相同的。


你可以一直这样做…

1
2
3
4
5
6
7
8
9
10
11
void YourMethod()
{
    if ( this.IsValid )
    {    
        Matrix matrix = new Matrix();
    }

    {
        Matrix matrix = new Matrix();
    }
}

…每套大括号{}允许您嵌套另一个级别的作用域。您遇到的问题是,嵌套作用域包括其父作用域的作用域。如果您声明一个兄弟范围,它将能够在同一个父范围内恢复变量。但正如其他人所指出的,这在以后可能会变得混乱。


想象一个人正在读这段代码。

从另一个试图读取代码的开发人员的角度来看,是否可以看到使用相同名称的两个不同变量有多混乱?即使它们代表的是同一事物,处理两个同名事物也太难了。


1
2
3
4
5
6
Matrix matrix = new Matrix();

if ( this.IsValid )
{
    Matrix matrix = new Matrix();
}

想象一下,它是这样写的,这是更明显的一点,我会想为什么这是不允许的,因为第二个实例显然应该被视为冲突。无法访问子范围内的外部变量将是很糟糕的。

从msdn"a:这是正确的行为,在语言规范的第3.7节中有介绍。它说,"局部变量声明(8.5.1)中声明的局部变量的范围是声明发生的块。"…。这种行为被认为是为了减少不正确地重复使用变量名的可能性(例如在剪切粘贴中)。(http://blogs.msdn.com/b/csharpfaq/archive/2004/05/18/why-can-t-i-use-the-same-variable-as-an-inner-loop-do.aspx)