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

比如:

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

编译器警告我说:

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

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

更新: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; } } } 

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

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

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

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

 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+names/

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

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

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

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

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

强调补充说。

并且,从第3.3节:

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

强调补充说。

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

你总能这样做……

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

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

想象一下,人类试图阅读这段代码。

从另一个尝试阅读代码的开发人员的角度来看,你能看到两个不同的同名变量有多么混乱吗? 即使他们代表同样的事情,也很难处理同名的两件事。

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

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

从MSDN“A:这是正确的行为,并在语言规范的3.7节中介绍。它说,”在local-variable-declaration(8.5.1)中声明的局部变量的范围是其中的块声明发生在“。” ……“这种行为意图不太可能重复使用变量名称(例如剪切和粘贴)。” (http://blogs.msdn.com/b/csharpfaq/archive/2004/05/18/why-can-ti-use-the-same-variable-as-an-inner-loop-does.aspx)