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

Joa*_*nge 15 .net c# compiler-construction

例如:

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

Matrix matrix = new Matrix();
Run Code Online (Sandbox Code Playgroud)

编译器警告我说:

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

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

Eri*_*ert 16

更新:2011年以下的答案对于早期版本的C#是正确的; 在更新的版本中,规则描述的答案已从C#中删除.设计团队确定该规则引起开发人员之间更多的混淆,导致像这样的问题,而不是有缺陷的程序可以保证,即使在我大大改进了错误消息以更清楚地诊断问题之后.


到目前为止给出的答案非常令人困惑.通过阅读错误消息开始正确分析问题.错误消息告诉您实际上是错误的:

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

仔细阅读.它正在告诉您C#的哪个规则被违反,即不允许您使用相同的名称来引用同一范围内的两个不同的东西.(实际上,错误信息有点错误;它应该说"局部变量声明空间",它表示"范围",但这是相当罗嗦的.)

此规则记录在C#4.0规范的第7.6.2.1节中:简单名称,块中的不变含义.

(在重叠声明空间中有两个同名的局部变量也是非法的.编译器也可能报告该错误,但在这种情况下报告更一般的错误.)

这些变量不是在不同的范围内,所以我无法从if语句外部访问第一个矩阵吗?

是.这种说法是正确的,无关紧要.这里的错误是在同一个局部变量声明空间中使用相同的简单名称来引用两个不同的东西.

考虑这种情况:

class C 
{
    int x;
    void M()
    {
        x = 10; // means "this.x"
        for(whatever)
        {
            int x = whatever;
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

同样的交易.这里的错误是在外部声明空间中使用简单名称"x"来引用this.x,并且在内部声明空间中使用它来表示"局部变量".使用相同的简单名称在同一个声明空间中引用两个不同的东西 - 记住,内部声明空间是外部声明空间的一部分 - 既令人困惑危险,因此是非法的.

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

class C 
{
    int x;
    void M()
    {
        int x;
        x = 10; // no longer means "this.x"
        for(whatever)
        {
            x = whatever;
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

如果首次使用简单名称的声明空格不重叠,那么简单名称引用不同的东西是合法的:

class C 
{
    int x;
    void M()
    {
        {
            x = 10; // means "this.x"
        }
        for(whatever)
        {
            int x = whatever; // Legal; now the 
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

欲了解更多信息,以及关于油炸食品的有趣故事,请参阅

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

  • @Joan:回答你的问题:我曾经花了差不多一整天的时间来追踪一个错误,结果发现我在一个非常漫长,复杂的C++例程中意外地使用了"i"来表示两个不同的东西.完全不明显的是,当我在调试器中逐步执行它时,调试器中的突然"i"指的是一个*不同的变量*(有时具有相同的值!),因为我走来走去.这是一个愚蠢的愚蠢错误,但是一个非常容易引入的bug,C++编译器对我没有任何帮助.C#中的规则更有可能促进良好的代码. (2认同)

Bru*_*ant 7

我相信这样做是为了避免模糊的错误或难以阅读的代码.

在方法范围和子范围之间使用相同的变量名称可能导致代码非常难以阅读,因为变量类型,更糟糕的是,意义可以改变,并且对读者的唯一提示将是类型声明关键字之前的变量.

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

实际上,您可以在MSDN上找到它:

名称的范围是程序文本的区域,在该区域内可以引用由名称声明的实体而无需对名称进行限定.范围可以嵌套,内部范围可以从外部范围重新声明名称的含义.(但是,这并没有消除3.3节规定的限制,即在嵌套块中,不可能声明一个与封闭块中的局部变量同名的局部变量.)外部作用域的名称是据说隐藏在内部范围覆盖的程序文本区域中,只有通过限定名称才能访问外部名称.

强调补充说.

并且,从第3.3节:

每个块或开关块为局部变量和常量创建不同的声明空间.通过local-variable-declarations和local-constant-declarations将名称引入此声明空间.如果块是实例构造函数,方法或运算符声明的主体,或索引器声明的get或set访问器,则在此类声明中声明的参数是块的局部变量声明空间的成员.块的局部变量声明空间包括任何嵌套块. 因此,在嵌套块中,不可能声明一个与封闭块中的局部变量同名的局部变量.

强调补充说.

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


Mat*_*ted 5

你总能这样做......

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

    {
        Matrix matrix = new Matrix(); 
    }
}
Run Code Online (Sandbox Code Playgroud)

...每组括号{}允许您嵌套另一个范围的范围.您遇到的问题是嵌套范围包括其父级的范围.如果声明siblng范围,它将能够在同一父级中重用变量.但正如其他人所指出的,这可能会在以后变得混乱.