C#中的可变范围混淆

时间:2009-07-28 21:46:43

标签: c# scope

我有两个代码示例。第一个不编译,但第二个编译。

代码示例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?

4 个答案:

答案 0 :(得分:44)

这里有两个相关规则。

第一个相关规则是:

  

本地变量是错误的   声明空间和嵌套的本地   要包含的变量声明空间   具有相同名称的元素。

(此页面上的另一个答案调出了规范中我们再次调用它的另一个位置。)

仅此一项就足以使其成为非法行为,但实际上第二条规则使其成为非法行为。

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

  

对于给定的每次出现   标识符作为简单名称   表达式或声明者,在   局部变量声明空间,   立即封闭,或   那个事件的开关块,   每次出现都是如此   标识符作为简单名称   表达式或声明者   立即封闭或阻止   switch-block必须指的是相同的   实体。这个规则确保了   名称的含义始终相同   在给定的块内,切换块,   for-,foreach-或using-statement,或   匿名函数。

你还需要知道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中的不一致用法视为相关。

此外,没有任何声明空格以非法方式重叠。

因此,这是合法的。

答案 1 :(得分:12)

来自C#语言规范......

  

声明的局部变量的范围   在本地变量声明中是   声明发生的块。   引用本地是错误的   变量在文本位置   在local-variable-declarator之前   局部变量。内   局部变量的范围,它是一个   编译时错误以声明另一个   局部变量或常数   同名。

在代码示例1中,i和objX都在函数范围内声明,因此该函数内任何块中的其他变量都不能与它们共享名称。在代码示例2中,两个objX都在for循环内部声明,这意味着它们不违反不在另一个声明的内部作用域中重新声明局部变量的规则。

答案 2 :(得分:6)

您可以在非重叠范围中使用相同的变量名称。但是,如果一个范围与另一个范围重叠,则不能在两个范围内声明相同的变量。这样做的原因是为了防止您在内部作用域中意外使用已经使用过的变量名,就像在第一个示例中使用i一样。这并不是真的要防止objX错误,因为这肯定不会让人感到困惑,但错误是规则应用的结果。编译器将objX视为在其声明之前和之后声明的范围内具有出处,而不仅仅是之后。

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

就个人而言,我发现这个错误令人讨厌,并且更喜欢C / C ++允许你做任何你想做的事情,混淆被诅咒。

答案 3 :(得分:-1)

您不应该在第二个示例中遇到编译错误。尝试将变量重命名为不同的字母/名称并重新编译,因为它可能是代码的其他问题,很可能您错过了大括号并更改了变量范围范围。