C# C语言中的变量范围混淆#

C# C语言中的变量范围混淆#,c#,scope,C#,Scope,我有两个代码示例。第一个不编译,但第二个编译 代码示例1(不编译) 如果变量范围的简单规则适用于代码示例2中,那么为什么这些规则不适用于代码示例1?允许在非重叠范围中使用相同的变量名称。但是,如果一个作用域与另一个作用域重叠,则不能在两个作用域中声明相同的变量。这样做的原因是为了防止在内部作用域中意外使用已使用的变量名,就像第一个示例中使用i时一样。这并不是真的要防止objX的objX错误,因为不可否认,这不会让人很困惑,但错误是规则应用方式的结果。编译器将objX视为在其声明之前和之后,而不仅

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

代码示例1(不编译)


如果变量范围的简单规则适用于代码示例2中,那么为什么这些规则不适用于代码示例1?

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


在第二个示例中,for的两个
循环具有独立、不重叠的作用域,因此您可以在第二个循环中自由地重复使用
i
objX
。这也是您可以重复使用
x
作为循环计数器的原因。显然,如果必须为(i=1;i的每个
都编不同的名称,这将是一个愚蠢的限制。这里有两个相关的规则

第一条相关规则是:

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

(本页上的另一个答案将在规范中的另一个位置再次调用。)

单凭这一点就足以使其违法,但事实上,第二条规则使其违法

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

对于给定事件的每次出现 标识符,作为 表达式或声明器,在 局部变量声明空间, 立即封闭块,或 该事件的开关块, 每发生一次相同的情况 标识符,作为 中的表达式或声明符 立即封闭块或 开关块必须参照相同的标准 实体。此规则确保 名字的意思总是一样的 在给定的块内,开关块, 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
public void MyMethod()
{ // 1
int i=10;//i1
{//2—不可见大括号
对于C语言规范中的(intx=10;x)

声明的局部变量的范围 在局部变量声明中 发生声明的块。 引用本地文件是错误的 位于文本位置的变量 位于局部变量声明符之前 局部变量的。在 局部变量的作用域,它是 声明另一个的编译时错误 带有 同名


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

第二个示例不应出现编译错误。请尝试将变量重命名为不同的字母/名称,然后重新编译,因为代码可能存在其他问题,您很可能错过了一个花括号并更改了变量的范围。

您应该阅读Eric Lippert的文章“C++和绝望之坑”这将有助于解释为什么C#设计团队会做出这样的决定。我特别喜欢这句话:“当我问你们‘直觉上显而易见’的事情是什么时,大部分人都不同意!”谢谢,这是非常描述性的。作为一个规则,它很容易记住…但是我无法理解为什么它是有限的,wrt first code和objX:我在一个块中创建了一个变量,当我从一个循环中出来时,它应该完成,就像我不能在块外访问它一样。这样看。移动声明应该是合法的如果你按照你的建议做了,那么,那就有时是合法的,有时是非法的,但是我们真正想要避免的是C++中发生的事情——C++中,有时移动变量声明实际上改变了其他简单名称的绑定。s!他清楚地说明了第二个样本是编译的,但第一个样本不是。我想这就是为什么你对此投了反对票。
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();
    }
}
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
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