我已经使用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循环使用的迭代器变量仅在其范围内有效。
答案 0 :(得分:119)
不允许在for循环和for循环之外定义具有相同名称的变量的原因是因为外部作用域中的变量在内部作用域中有效。这意味着如果允许的话,for循环中会有两个'i'变量。
请参阅:MSDN Scopes
具体做法是:
在local-variable-declaration中声明的局部变量的范围 (第8.5.1节)是声明发生的块。
和
在for的初始化器中声明的局部变量的范围 声明(第8.8.3节)是for-initializer,for-condition, for-iterator和for语句的包含语句。
还有:Local variable declarations(C#规范的第8.5.1节)
具体做法是:
在local-variable-declaration中声明的局部变量的范围 是声明发生的块。引用是一个错误 到位于文本位置之前的局部变量 局部变量的local-variable-declarator。 在范围内 局部变量,声明另一个本地是一个编译时错误 变量或常数同名。
(强调我的。)
这意味着for循环中i
的范围是for循环。而for循环外i
的范围是整个主方法加上 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
相比,它的可维护性和可读性肯定要小得多。
<强> N.B:强>
请注意,C#的范围规则与C++'s scoping rules不同。在C ++中,变量仅在声明它们的范围内,直到块的结尾。这将使您的代码成为C ++中的有效构造。
答案 1 :(得分:29)
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/。
答案 2 :(得分:13)
在循环之后,有一种方法可以在方法中声明和使用i
:
static void Main()
{
for (int i = 0; i < 5; i++)
{
}
{
int i = 4;
int A = i;
}
}
你可以用Java做到这一点(它可能来自C我不确定)。为了变量名,它当然有点乱。
答案 3 :(得分:7)
如果您在 i
循环之前声明了for
,您是否认为在循环中声明它仍然有效?
不,因为那时两者的范围会重叠。
至于无法执行int A=i;
,这只是因为i
仅存在于for
循环中,就像应该这样做。
答案 4 :(得分:7)
除了J.Kommer的答案(+1 btw)。在NET范围的标准中有这个:
块如果在块构造(如If语句)中声明变量,则该变量的作用域仅在块结束之前。生命周期一直持续到程序结束。
过程如果在过程中声明变量,但在任何If语句之外,则范围为End Sub或End 功能。变量的生命周期是程序结束。
因此,for循环标头中的int i decalared仅在for循环块期间在范围内, BUT 它的生命周期将持续到Main()
代码完成。
答案 5 :(得分:5)
考虑这一点的最简单方法是将I的外部声明移到循环之上。那应该是显而易见的。
无论哪种方式都是相同的范围,因此无法完成。
答案 6 :(得分:4)
此外,C#的规则在严格编程方面很多时候都没有必要,但是要保持代码的清洁和可读性。
例如,他们可以做到这一点,如果你在循环之后定义它然后就可以了,但是有人读取你的代码并错过定义行可能会认为它与循环的变量有关。答案 7 :(得分:2)
Kommer的答案在技术上是正确的。让我用生动的盲屏比喻来解释它。
for-block和封闭外部块之间存在单向盲屏,以便for-block中的代码可以看到外部代码,但外部块中的代码无法看到内部代码。
由于外部代码无法看到内部,因此无法使用内部声明的任何内容。但是由于for-block中的代码可以在内部和外部都看到,因此在两个地方声明的变量都不能通过名称明确地使用。
所以要么你没有看到它,要么你是C#!
答案 8 :(得分:0)
以与可以在int
块中声明using
相同的方式查看它:
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
希望这有助于可视化正在发生的事情。
我非常喜欢这个功能,因为从int
循环中声明for
可以保持代码的良好和紧密。