关于c#:在for循环中声明的变量是局部变量?

Variable declared in for-loop is local variable?

我使用C已经很长时间了,但从未意识到:

1
2
3
4
5
6
7
8
9
10
 public static void Main()
 {
     for (int i = 0; i < 5; i++)
     {

     }

     int i = 4;  //cannot declare as 'i' is declared in child scope                
     int A = i;  //cannot assign as 'i' does not exist in this context
 }

那么,如果for块不允许我声明具有此名称的变量,为什么我不能在for块之外使用值"i"?

我认为Afor循环使用的迭代器变量仅在其作用域中有效。


不允许在for循环和for循环外部定义同名变量的原因是外部范围中的变量在内部范围内有效。这意味着如果允许,for循环中会有两个"i"变量。

参见:msdn范围

明确地:

The scope of a local variable declared in a local-variable-declaration
(Section 8.5.1) is the block in which the declaration occurs.

The scope of a local variable declared in a for-initializer of a for
statement (Section 8.8.3) is the for-initializer, the for-condition,
the for-iterator, and the contained statement of the for statement.

以及:局部变量声明(C规范第8.5.1节)

明确地:

The scope of a local variable declared in a local-variable-declaration
is the block in which the declaration occurs. It is an error to refer
to a local variable in a textual position that precedes the
local-variable-declarator of the local variable. Within the scope of a
local variable, it is a compile-time error to declare another local
variable or constant with the same name.

(强调我的)

也就是说,for循环中的i的作用域是for循环。而在for循环之外的i的范围是整个主方法加上for循环。这意味着在循环中会出现两次i,根据上面的描述,这是无效的。

不允许您执行int A = i;的原因是因为int i的范围仅限于在for循环中使用。因此,在for循环之外,无法再访问它。

如您所见,这两个问题都是范围界定的结果;第一个问题(int i = 4;将导致for循环范围内的两个i变量。而int A = i;将导致访问超出范围的变量。

您可以改为声明i的作用域是整个方法的作用域,然后在方法和for循环作用域中使用它。这将避免违反任何规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void Main()
{
    int i;

    for (i = 0; i < 5; i++)
    {

    }

    // 'i' is only declared in the method scope now,
    // no longer in the child scope -> valid.
    i = 4;

    // 'i' is declared in the method's scope -> valid.
    int A = i;
}

编辑:

当然,可以更改C编译器,以使此代码能够非常有效地编译。所有这些都是有效的:

1
2
3
4
5
6
7
8
9
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

for (int i = 5; i > 0; i--)
{
    Console.WriteLine(i);
}

但是,能够编写代码是否真的有利于代码的可读性和可维护性,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void Main()
{
    int i = 4;

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
    }

    for (int i = 5; i > 0; i--)
    {
        Console.WriteLine(i);
    }

    Console.WriteLine(i);
}

想想这里可能出现的错误,最后一个i是打印出0还是4?现在,这是一个非常小的例子,很容易跟踪和跟踪,但是与用不同的名称声明外部i相比,它显然要低很多维护性和可读性。

N. B:

请注意,C++的范围规则与C++的范围规则不同。在C++中,变量只在它们声明的范围内,直到块的结尾。这将使您的代码在C++中成为一个有效的构造。


J.Kommer的回答是正确的:简而言之,在局部变量声明空间中声明一个局部变量与另一个局部变量声明空间重叠是非法的,该局部变量声明空间具有相同名称。

这里还违反了C的附加规则。另一个规则是,使用简单名称引用两个不同重叠的局部变量声明空间中的两个不同实体是非法的。因此,您的示例不仅是非法的,而且也是非法的:

1
2
3
4
5
6
7
8
9
class C
{
    int x;
    void M()
    {
        int y = x;
        if(whatever)
        {
            int x = 123;

因为现在在"y"的局部变量声明空间中使用了简单的名称"x",表示两个不同的东西——"this.x"和局部"x"。

有关这些问题的更多分析,请参阅http://blogs.msdn.com/b/ericlippert/archive/tags/simple+name/。


有一种方法在循环之后声明并使用EDCOX1 0中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

    {
        int i = 4;
        int A = i;
    }
}

你可以用Java来做这件事(它可能起源于C,我不确定)。当然,这是一个有点混乱的变量名。


除了J.Kommer的答案(+1 btw)。在网络范围标准中有这样的规定:

block If you declare a variable within a block construct such as an If statement, that variable's scope is only until the end of the block. The lifetime is until the procedure ends.

Procedure If you declare a variable within a procedure, but outside of any If statement, the scope is until the End Sub or End
Function. The lifetime of the variable is until the procedures ends.

因此,for循环标头中的int i将仅在for循环块中处于范围内,但是它的生存期一直持续到EDCOX1代码19代码完成为止。


如果你在你的for循环之前声明了i,你认为在循环中声明它仍然有效吗?

不,因为那样的话,两者的范围就会重叠。

至于不能做int A=i;,那只是因为i只存在于for循环中,就像它应该做的那样。


考虑到这一点最简单的方法是将I的外部声明移动到循环上方。那就应该很明显了。

无论哪种方式,它都是相同的范围,因此无法完成。


此外,C的规则在严格编程方面是不必要的,但是否有助于保持代码的干净和可读性。

例如,他们可以这样做,如果您在循环之后定义它,那么它是可以的,但是,如果有人读取您的代码并错过了定义行,那么可能会认为它与循环的变量有关。


科默的回答在技术上是正确的。让我用一个生动的盲屏隐喻来解释它。

在for块和封闭的外部块之间有一个单向盲屏,以便for块中的代码可以看到外部代码,但外部块中的代码看不到内部代码。

因为外部代码看不到内部,所以它不能使用内部声明的任何内容。但是,由于for块中的代码可以看到内部和外部,因此在这两个位置声明的变量不能明确地按名称使用。

所以要么你看不到,要么你C!


以同样的方式查看它,就好像您可以在using块中声明一个int

1
2
3
4
using (int i = 0) {
  // i is in scope here
}
// here, i is out of scope

但是,由于int没有实现IDisposable的功能,因此无法实现。不过,它可能有助于人们可视化如何将int变量放置在私有范围内。

另一种说法是,

1
2
3
4
5
if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

希望这有助于可视化正在发生的事情。

我真的很喜欢这个特性,因为从for循环内部声明int可以保持代码的美观和紧凑。