在for循环中声明的变量是局部变量吗?


133

我已经使用C#很长时间了,但从未意识到以下几点:

 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块之外使用'i'值呢?

我认为for循环使用的迭代器变量仅在其范围内有效。


8
由于外部块范围包括for循环的范围
V4Vendetta

3
我想到它的一种方式(某些代码样式指南对此有所要求,尤其是对于动态类型的语言而言)是,可以在该范围的开头声明在一个范围中声明的所有变量,这意味着可以重写您的函数int i, A; for(int i = 0; i < 5; i++){ } i=4; A=i
Keith

2
@ V4Vendetta:相反。内部块是父块的黑匣子。
塞巴斯蒂安·马赫

4
除了无法做到这一点的技术原因之外,为什么这(或它的变体)会成为明智的选择呢?它显然有不同的用途,因此请给它起一个不同的名称。
戴夫

我根本没有意识到这一点,但似乎完全是愚蠢的限制。
alan2here 2011年

Answers:


119

不允许在for循环内和for循环外定义具有相同名称的变量的原因是,因为外部作用域内的变量在内部作用域内有效。意味着如果允许的话,for循环中将有两个“ i”变量。

请参阅:MSDN范围

特别:

在local-variable-declaration(第8.5.1节)中声明的局部变量的范围是声明所在的块。

在for语句的for-initializer中声明的局部变量的范围(第8.8.3节)是for初始化器,for-condition,for-iterator和for语句包含的语句。

并且:局部变量声明(C#规范的8.5.1节)

特别:

在local-variable-declaration中声明的局部变量的范围是声明所在的块。在文本位置引用局部变量的局部变量声明符之前的局部变量是错误的。在局部变量的范围内,声明另一个具有相同名称的局部变量或常量是编译时错误。

(强调我的。)

这意味着ifor循环内部的范围就是for循环。而ifor循环外部的范围是整个主要方法加上 for循环。这意味着您将i在循环中出现两次,根据上述情况,这是无效的。

不允许您这样做int A = i;的原因int i是,仅在for循环内使用。因此,不再可以在for循环外部访问它。

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

相反,您可以做的是声明i要作用于整个方法,然后在方法和for循环作用域中使用它。这样可以避免违反任何一条规则。

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#编译器以允许此代码相当有效地进行编译。毕竟这是有效的:

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

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

但是能够编写如下代码对您的代码可读性和可维护性真的有好处:

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使用不同名称声明外部相比,它绝对缺乏可维护性和可读性。

注意:

请注意,C#的范围规则与C ++的范围规则不同。在C ++中,变量仅在声明变量直至块结束的范围内。这将使您的代码成为C ++中的有效构造。


有道理,i尽管如此,我认为它应该在内部变量上出错;对我来说,这似乎更明显。
乔治·达基特

但是范围是命令及其{}的作用?还是说其父{}?
约翰五世

2
好吧,如果我在for语句之后声明'A',则它在for循环中无效,因为稍后会声明。这就是为什么我不明白为什么不能使用相同名称的原因。
约翰五世

9
为了完整起见,也许这个答案应该指出,在这种情况下,C#范围规则与C ++的规则不同。在C ++中,变量仅在声明变量的范围内直至块的末尾(请参阅msdn.microsoft.com/zh-cn/library/b7kfh662(v=vs.80).aspx)。
AAT

2
请注意,Java在C ++和C#之间采用了一种中间方法:因为OP的示例在Java中是有效的,但是如果将外部i定义移至for循环之前,则内部i定义将被标记为无效。
Nicola Musatti 2011年

29

J.Kommer的答案是正确的:简单地说,它是非法的局部变量在声明局部变量声明空间重叠的另一个局部变量声明空间,有一个本地的同名。

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

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+names/


2
另外,有趣的是,您在进行更改时将编译示例代码int y = this.x;
Phil,

4
@菲尔:对。this.x不是一个简单的名字
埃里克·利珀特

13

i循环后有一种在方法内部声明和使用的方法:

static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

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

您可以在Java中执行此操作(不确定,它可能源自C)。为了使用变量名,这当然有些混乱。


是的,这源自C / C ++。
Branko Dimitrijevic

1
我以前不知道 整洁的语言功能!

@MathiasLykkegaardLorenzen是的
克里斯·S

7

如果你想宣布i 之前for循环,你认为它应该仍然有效宣布它在循环中?

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

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


7

除了J.Kommer的答案(+1 btw)。NET范围的标准中包含以下内容:

如果在诸如If语句之类的块结构中声明变量,则该变量的作用域仅到块末尾为止。生命周期直到过程结束。

过程 如果在过程内但在任何If语句之外声明变量,则作用域直到End Sub或End Function为止。变量的生命周期直到过程结束。

因此,仅在for循环块期间,在for循环头中声明的int仅在作用域内,其生存期将持续到Main()代码完成为止。


5

考虑这一点的最简单方法是将I的外部声明移到循环上方。它将变得显而易见。

两种方法都具有相同的作用域,因此无法完成。


4

同样,在严格的编程方面,C#的规则在很多时候都不是必需的,但是可以保证代码的清洁和可读性。

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


2

Kommer的答案在技术上是正确的。让我用一个生动的盲目隐喻来解释它。

在for块和封闭的外部块之间存在一种单向盲屏,因此for块中的代码可以看到外部代码,但是外部块中的代码看不到内部代码。

由于外部代码看不到inside,因此它无法使用内部声明的任何内容。但是由于for块中的代码可以在内部和外部看到,因此不能在名称上明确使用在两个地方声明的变量。

因此,要么您看不到它,要么您C#!


0

以与可以intusing块中声明an 相同的方式查看它:

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

但是,由于int未实现IDisposable,因此无法完成。但是,它可以帮助某人可视化如何将int变量放置在私有范围内。

换句话说,

if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

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

我真的很喜欢这个功能,因为intfor循环内部声明from 可以使代码保持美观和紧凑。


WTF?如果要标记NEG,请用球说出原因。
jp2code 2011年

不是贬低者,我认为与之进行比较using是可以的,尽管其语义是完全不同的(这也许就是为什么减低了它的原因)。请注意,这if (true)是多余的。删除它,您将看到一个作用域。
亚伯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.