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

我已经使用 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 块不允许我用这个名称声明一个变量,为什么我不能在 for 块之外使用‘ i’的值呢?

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

12380 次浏览

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

见: MSDN 作用域

具体来说:

局部变量声明中声明的局部变量的范围 (第8.5.1节)是发生声明的块。

还有

在 for 的 for 初始值设定项中声明的局部变量的范围 语句(第8.8.3节)是 for 初始化程序,for 条件, For-iterator 和 for 语句的包含语句。

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

具体来说:

局部变量声明中声明的局部变量的范围 是发生声明的块 属性之前的文本位置中的局部变量 局部变量的 local- 变量-声明程序。 < strong > 在 局部变量,则声明另一个局部变量是编译时错误 同名变量或常量。

(重点是我的)

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

之所以不允许执行 int A = i;,是因为 int i只能在 for循环中使用。因此,它不再能够在 for循环之外访问。

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

相反,您可以声明 i作用域为整个方法,然后在方法和 for-loop 作用域中使用它。这将避免违反任何一条规则。

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 的外部声明移动到循环的上方。那就很明显了。

无论哪种方式都是相同的范围,因此不能这样做。

如果你声明 i 之前你的 for循环,你认为它应该仍然是有效的声明内循环?

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

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

另外,C # 的规则在很多时候对于严格的编程来说是不必要的,但是它们可以保持代码的整洁和可读性。

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

除了 J.Kommer 的回答(+ 1 btw) ,在 NET 范围的标准中还有这个:

Block 如果你在一个块结构中声明一个变量,比如 If 语句,那么这个变量的作用域只能到这个块的结尾。生存期直到过程结束。

过程 如果您在过程中声明了一个变量,但是在任何 If 语句之外,则作用域直到 EndSub 或 End 变量的生存期直到过程结束为止。

因此在 for 循环头中标记的 int i 只在 for 循环块中有效,但是的生命周期一直持续到 Main()代码完成。

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

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


}


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

你可以在 Java 中这样做(它可能源自 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可以使代码保持漂亮和紧凑。

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

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

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

因为现在简单的名称“ x”已经在“ y”的局部变量声明空间中被用来表示两个不同的东西——“ this. x”和局部“ x”。

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

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

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

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

所以要么你没看出来,要么你就是 C # !