C#中可变范围的混淆
我有两个代码示例。 第一个不编译,但第二个。
代码示例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内部的组织,那么为什么第二个代码示例编译?
代码示例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中应用了variables范围的简单规则,为什么这些规则不适用于代码示例1?
这里有两个相关的规则。
第一个相关的规则是:
局部variables声明空间和嵌套局部variables声明空间包含具有相同名称的元素是错误的。
(在这个页面上的另一个答案在规范中调用另一个位置,我们再次调用这个位置。)
只有这一点足以使这个非法,但实际上第二个规则是非法的。
C#中的第二个相关规则是:
对于给定标识符的每次出现,作为expression式或声明符中的简单名称,在局部variables声明空间内,立即封闭该事件的块或开关块,每隔一次出现与简单名称相同的标识符直接封闭块或开关块内的expression式或声明符必须引用同一个实体。 这个规则可以确保在一个给定的块,开关块,for-,foreach或using语句或者匿名函数中,名字的含义总是相同的。
你还需要知道,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
你有三个“简单的名字”,我,X和OBJX。 你有五个variables,我标记为i1,x2,i3,objX3和objX1。
包含i和objX用法的最外面的块是块1.因此,在块1中,我和objX必须始终指向相同的东西。 但他们没有。 有时我指的是i1,有时指的是i3。 同objX一样。
x,但是,只有在每个块中意味着x2。
另外,“i”variables和“objX”variables都在同一个局部variables声明空间中。
因此,这个程序在几个方面是一个错误。
在你的第二个程序中:
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
现在你又有三个简单的名字,六个variables。
首先包含简单名称x的用法的最外面的块是块2和块4.在块2中,x指的是x2。 在整个块4中,x指的是x4。 所以这是合法的。 与我和objX一样 – 它们在方块3和方块5中使用,并且在每个方块中表示不同的东西。 但是在同一个块中,没有任何一个简单的名字用来表示两个不同的东西。
现在,您可能会注意到, 考虑到块1的全部 ,x用于表示x2和x4。 但是没有提到在块1内的x,而不在另一个块内。 因此,我们不会将第一部分的不一致用法计为相关的。
而且,没有一个声明空间以非法的方式重叠。
所以这是合法的。
从C#语言规范…
局部variables声明中声明的局部variables的范围是声明出现的块。 在局部variables的局部variables声明符之前的文本位置引用局部variables是错误的。 在局部variables的范围内,声明另一个局部variables或具有相同名称的常量是编译时错误。
在代码示例1中,i和objX都在函数的范围内声明,因此该函数内的任何块中都不能与其他variables共享一个名称。 在代码示例2中,两个objXs都是在for循环中声明的,这意味着它们不违反不从另一个声明中重新声明内部作用域中局部variables的规则。
您可以在不重叠的作用域中使用相同的variables名称。 但是,如果一个范围与另一个范围重叠,则不能在两者中声明相同的variables。 原因是为了防止你在内部范围内意外地使用一个已经使用的variables名,就像你在第一个例子中使用i
一样。 这并不是真的要防止objX
错误,因为那肯定不会是很混乱,但是错误是这个规则被应用的结果。 编译器将objX
视为在其声明之前和之后声明的范围内,而不仅仅是声明之后。
在第二个示例中,两个for
循环具有独立的,不重叠的作用域,因此您可以在第二个循环中自由地重用i
和objX
。 这也是您可以重复使用x
作为循环计数器的原因。 显然,如果你不得不为函数中的for(i=1;i<10;++i)
样式循环中的每个名称设置不同的名字,那将是一个愚蠢的限制。
就个人而言,我觉得这个错误很烦人,而且更喜欢C / C ++的方式,让你做任何你想做的事,混淆就该死。
你不应该得到第二个样本的编译错误。 尝试将variables重命名为不同的字母/名称,并重新编译,因为它可能是其他问题,最有可能你错过了一个大括号,并更改variables范围的范围。