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

本文关键字:变量 局部变量 声明 循环 for | 更新日期: 2023-09-27 18:13:29

我已经使用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循环使用的迭代器变量仅在其作用域中有效。

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

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

参见:MSDN作用域

专:

local-variable-declaration中声明的局部变量的作用域(第8.5.1节)是声明发生的块。

在for的for初始化式中声明的局部变量的作用域语句(第8.8.3节)是for初始化式,for条件,for迭代器,以及for语句所包含的语句。

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

专:

在local-variable-declaration中声明的局部变量的作用域是发生声明的块。引用错误的文本位置上的局部变量局部变量的声明器。在a的作用域中声明另一个局部变量会导致编译时错误

(我强调。)

这意味着for循环内的i的作用域是for循环。而for循环之外的i的作用域是整个主方法加上for循环的。这意味着你会在循环中出现两次i,这是无效的。

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

正如你所看到的,这两个问题都是作用域的结果;第一个问题(int i = 4;)将导致在for循环作用域中出现两个i变量。而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

N。B:

请注意,c#的作用域规则不同于c++的作用域规则。在c++中,变量的作用域从声明位置一直到代码块结束。这将使您的代码在c++中成为有效的结构。

Kommer的回答是正确的:简单地说,在局部变量声明空间中声明局部变量是非法的,如果另一个局部变量声明空间有相同名称的局部变量声明空间重叠。

这里还违反了c#的另一条规则。额外的规则是在两个不同的重叠局部变量声明空间中使用一个简单的名称来引用两个不同的实体是非法的。所以不仅你的例子是非法的,这个也是非法的:

class C
{
    int x;
    void M()
    {
        int y = x;
        if(whatever)
        {
            int x = 123;

因为现在简单的名字"x"在"y"的局部变量声明空间中被用来表示两个不同的东西——"this"。和本地的" X "

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

有一种方法可以在循环之后的方法中声明和使用i:

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

你可以在Java中这样做(它可能起源于C,我不确定)。当然,为了变量名的缘故,这有点混乱。

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

不可以,因为那样的话两者的作用域会重叠。

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

除了J.Kommer的回答(顺便说一句)。在。NET作用域的标准中是这样的:

block如果在块结构(如If语句)中声明变量,则该变量的作用域仅到块末尾。生存期是指直到过程结束。

Procedure如果在过程中声明变量,但在任何If语句之外,则作用域直到End Sub或End函数。变量的生命周期是直到过程结束。

因此,在for循环头中声明的int i将仅在for循环块的作用域中,但是它的生存期将持续到Main()代码完成。

考虑这个问题的最简单的方法是将I的外部声明移到循环上方。这应该是显而易见的。

这两种方式都是相同的作用域,因此不能这样做。

c#的规则在很多时候对于严格的编程来说是不必要的,但是它们是用来保持你的代码干净和可读的。

例如

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

Kommer的回答在技术上是正确的。让我用一个生动的盲幕比喻来解释一下。

在for块和封闭的外部块之间有一个单向盲屏,使得for块内部的代码可以看到外部代码,而外部块中的代码无法看到内部代码。

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

所以要么你看不见,要么你c# !

就像可以using块中声明int一样:

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

希望这能帮助你想象发生了什么。

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