2009-07-28 53 views
14

我有两个代码示例。第一个不编译,但第二个编译。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应用,那么我们为什么不这些规则同样适用于代码示例1?

回答

39

这里有两条相关规则。

的第一个相关的规则是:

它是一个局部变量 声明空间和嵌套的局部 变量声明空间包含 元素具有相同名称的错误。

(而这个页面上的其他答案召唤出在我们再次调用此超出规定的其他位置。)

这仅仅是足以让这个非法的,但实际上第二个规则,使这一非法。

C#中的第二相关规则是:

对于在 表达式或声明给定 标识符作为简单名称的每次出现的 局部变量声明空间, 立即封闭块内,或者发生, 相同 标识符作为简单名称在 内的 表达式或声明每次其它出现时的 开关块立即封闭块或 开关 - 块必须参考相同的实体 。此规则确保名称含义在给定块,开关块, 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 

你有三个 “简单名称”,I,X和物objx。你有五个变量,我标记为i1,x2,i3,objX3和objX1。

包含i和objX用法的最外面的块是块1.因此,在块1中,我和objX必须始终引用相同的东西。但他们没有。有时我指的是i1,有时它指的是i3。与objX一样。

然而,在每个块中只有x2意味着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。因此,这是合法的。与我和objX一样 - 它们在方块3和5中使用,并且在每个方块中表示不同的东西。但是在同一个块中,没有任何一个简单的名字用来表示两个不同的东西。

现在,您可能会注意到考虑到块1的全部,x用于表示x2和x4。但是没有提到块1中的x,但不包含另一个块中的x。因此,我们不会将第1部分中的不一致用法计为相关。

此外,没有一个声明空间以非法的方式重叠。

因此,这是合法的。

+0

谢谢你的描述。 – 2009-07-28 22:31:28

+0

它很容易记住它作为一个规则...但一些如何不能消化为什么这是有限的,第一个代码和objX:我创建了一个块的变量,当我走出一个循环时,它打破了完成,就像我不能在块外访问它一样。 – 2009-07-28 22:36:28

6

您可以在非重叠范围中使用相同的变量名称。但是,如果一个范围与另一个范围重叠,则不能在两者中声明相同的变量。原因是为了防止您在内部范围内意外地使用已经使用的变量名,就像您在第一个示例中使用i一样。这并不是真的要阻止objX错误,因为这肯定不会造成混乱,但错误是应用规则的结果。编译器认为objX在其声明之前和之后声明的范围内都有出处,而不仅仅是声明之后。

在第二个示例中,两个for循环具有独立的,不重叠的示波器,因此您可以在第二个循环中自由地重新使用iobjX。这也是您可以重复使用x作为循环计数器的原因。显然,如果你必须为函数中的每个for(i=1;i<10;++i)样式循环组成不同的名称,那将是一个愚蠢的限制。

在个人笔记上,我发现这个错误令人讨厌,并且更喜欢C/C++的方式,让你做任何你想做的事情,困惑都会被诅咒。

+0

你应该阅读Eric Lippert的文章“C++和绝望之坑”。这将有助于解释为什么C#设计团队做出了他们所做的决定。我特别喜欢这句话:“当我问你们这些'直观明显'的事情是什么时候,很大一部分人不同意!” – 2009-07-28 21:59:12

12

从C#语言规范...

的局部变量的范围在一个局部变量声明声明 是该声明发生 块。 在局部变量的局部变量声明符 之前的文本位置引用局部变量 是错误的。在局部变量的 范围内,编译时错误为 另一个 局部变量或常数与 同名。

在代码样品1,i和物objx的函数的范围被声明,那么在该函数内的任何块没有其他变量可以与他们共享的名称。在代码示例2中,两个objX都是在for循环内部声明的,这意味着它们不违反不从另一个声明中重新声明内部作用域中局部变量的规则。

-1

你不应该得到第二个样本的编译错误。尝试将变量重命名为不同的字母/名称,并重新编译,因为它可能是其他问题,最有可能你错过了大括号并更改了变量范围。