我有两个代码示例。第一个不编译,但是第二个编译。
代码示例1 (不编译)
public void MyMethod(){ int i=10; for(int x=10; x<10; x++) { int i=10; // Point1: compiler reports error var objX = new MyOtherClass(); } var objX = new OtherClassOfMine(); // Point2: compiler reports error }
我了解为什么编译器会在上报告错误Point1。但我不明白为什么它会在上报告错误Point2。如果您说这是由于MSIL内部的组织造成的,那么为什么第二个代码示例会进行编译?
Point1
Point2
代码示例2 (编译)
public void MyMethod(){ for(int x=10; x<10; x++) { int i=10; var objX = new MyOtherClass(); } for(int x=10; x<10; x++) { int i=10; var objX = new MyOtherClass(); } }
如果变量范围的简单规则适用于代码示例2,那么为什么这些规则不适用于代码示例1?
这里有两个相关的规则。
第一条相关规则是:
局部变量声明空间和嵌套的局部变量声明空间包含相同名称的元素是错误的。
(此页面上的另一个答案将在规范中调出另一个位置,我们再次将其调出。)
仅此一项就足以使这种违法,但实际上,第二条规则使这种违法成为可能。
C#中的第二条相关规则是:
对于表达式或声明符中给定标识符的每个简单名称的每次出现,在局部变量声明空间内,立即将其所在的块或切换块括起来,在该示例中,每隔一次出现与简单名称相同的标识符直接封闭的块或switch- block中的表达式或声明符必须引用同一实体。该规则确保名称的含义在给定的块,切换块,for,foreach或using语句或匿名函数中始终相同。
(更新:此答案写于2009年;在C#的最新版本中,此规则已被删除,因为它被认为过于混乱;用户产生的混乱不值得一小部分被阻止的错误。有关详细信息,请参见此答案。 )
您还需要知道,将for循环视为好像整个事物周围都存在“不可见的花括号”一样。
现在我们知道了,让我们注释一下代码:
public void MyMethod() { // 1 int i=10; // i1 { // 2 -- invisible brace for(int x=10; x<10; x++) // x2 { // 3 int i=10; // i3 var objX = new MyOtherClass(); // objX3 } // 3 } // 2 var objX = new OtherClasOfMine(); // objX1 } // 1
您有三个“简单名称”,即i,x和objX。您有五个变量,我将它们标记为i1,x2,i3,objX3和objX1。
包含i和objX用法的最外面的块是块1。因此,在块1内,i和objX必须始终引用同一事物。但是他们没有。有时我指的是i1,有时指的是i3。与objX相同。
但是,x在每个块中仅表示x2。
同样,两个“ i”变量和两个“ objX”变量都在同一局部变量声明空间中。
因此,该程序在几种方面都是错误。
在第二个程序中:
public void MyMethod() { // 1 { // 2 -- invisible for(int x=10; x<10; x++) // x2 { // 3 int i=10; // i3 var objX = new MyOtherClass(); // objX3 } //3 } // 2 { // 4 -- invisible for(int x=10; x<10; x++) // x4 { // 5 int i=10; // i5 var objX = new MyOtherClass(); // objX5 } //5 } // 4 } // 1
现在,您又有了三个简单的名称和六个变量。
首先包含简单名称x用法的最外面的块是块2和4。在整个块2中,x表示x2。在整个方框4中,x表示x4。因此,这是合法的。与i和objX相同- 它们分别在第3块和第5块中使用,并且各自含义不同。但是,在同一块中,没有一个简单的名称用来表示两个不同的事物。
现在,您可能会注意到, 考虑到块1的全部 ,x用于表示x2和x4。但是没有提及x位于块1内,而没有提及另一个块内。因此,我们不会将块1中的不一致用法视为相关。
而且,没有任何声明空间以非法方式重叠。
因此,这是合法的。