for循环中声明的变量是局部变量吗?
Posted
技术标签:
【中文标题】for循环中声明的变量是局部变量吗?【英文标题】:Variable declared in for-loop is local variable? 【发布时间】:2011-12-20 23:55:01 【问题描述】:我已经使用 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循环的作用域 我认为的一种方式(一些代码风格指南要求这样做,尤其是对于动态类型语言)是在一个范围内声明的所有变量都可以在该范围的开头声明,这意味着你的函数可以重写int i, A; for(int i = 0; i < 5; i++) i=4; A=i
@V4Vendetta:反之亦然。内部块是父块的黑盒子。
除了不可能的技术原因之外,为什么这(或它的变体)永远是明智之举?!它显然有不同的用途,所以给它一个不同的名字。
我完全没有意识到这一点,但这似乎是一个完全愚蠢的限制。
【参考方案1】:
不允许在 for 循环和 for 循环外部定义同名变量的原因是,外部作用域中的变量在内部作用域中有效。这意味着如果允许,for 循环中将有两个“i”变量。
见:MSDN Scopes
具体来说:
在局部变量声明中声明的局部变量的范围 (第 8.5.1 节)是发生声明的块。
和
在 for 的初始化器中声明的局部变量的范围 语句(第 8.8.3 节)是 for-initializer,for-condition, for 迭代器,以及 for 语句的包含语句。
还有:Local variable declarations(C# 规范的第 8.5.1 节)
具体来说:
在局部变量声明中声明的局部变量的范围 是发生声明的块。 引用是错误的 到文本位置的局部变量 局部变量的local-variable-declarator。 范围内 局部变量,声明另一个局部变量是编译时错误 具有相同名称的变量或常量。
(强调我的。)
这意味着你的for循环中i
的范围是for循环。而 for 循环之外的 i
的范围是整个 main 方法加上 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
相比,它的可维护性和可读性肯定要低得多。
注意:
请注意,C# 的作用域规则不同于 C++'s scoping rules。在 C++ 中,变量仅在声明它们的范围内直到块结束。这将使您的代码成为 C++ 中的有效构造。
【讨论】:
有道理,我认为它应该在内部i
变量上出错;这对我来说似乎更明显。
但范围是命令及其?还是它的父级 ?
好吧,如果我在 for 语句之后声明 'A',它在 for 循环中是无效的,因为它稍后会声明。这就是为什么我不明白为什么不能使用相同的名称。
为了完整起见,也许这个答案应该指出在这种情况下 C# 范围规则与 C++ 的规则不同。在 C++ 中,变量仅在声明它们的范围内直到块结束(请参阅msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspx)。
请注意,Java 采用 C++ 和 C# 之间的中间方法:因为它是 OP 的示例在 Java 中是有效的,但如果外部 i
定义在 for 循环之前移动,则内部 @ 987654344@ 定义将被标记为无效。【参考方案2】:
考虑这一点的最简单方法是将 I 的外部声明移到循环上方。到时候应该就很明显了。
无论哪种方式都是相同的范围,因此无法完成。
【讨论】:
【参考方案3】:如果您在for
循环之前声明了i
之前,您认为在循环内声明它仍然有效吗?
不,因为那样两者的范围会重叠。
至于不能做int A=i;
,那只是因为i
只存在于for
循环中,就像它应该做的那样。
【讨论】:
【参考方案4】:此外,C# 的规则在严格的编程方面很多时候并不是必需的,但它们可以让您的代码保持干净和可读。
例如,他们可以做到这一点,这样如果您在循环之后定义它就可以了,但是阅读您的代码并错过定义行的人可能会认为它与循环的变量有关。
【讨论】:
【参考方案5】:除了 J.Kommer 的回答(+1 顺便说一句)。 NET 范围的标准中有这样的内容:
block 如果您在块结构(如 If 语句)中声明变量,则该变量的作用域仅在块结束之前。生命周期是直到过程结束。
过程 如果您在过程中声明变量,但在任何 If 语句之外,则范围是直到 End Sub 或 End 功能。变量的生命周期是直到过程结束。
因此,在 for 循环标头中声明的 int i 将仅在 for 循环块期间在范围内,但它的生命周期将持续到 Main()
代码完成。
【讨论】:
【参考方案6】:有一种方法可以在循环后的方法内部声明和使用i
:
static void Main()
for (int i = 0; i < 5; i++)
int i = 4;
int A = i;
您可以在 Java 中执行此操作(我不确定它可能源自 C)。为了变量名当然有点乱。
【讨论】:
是的,这源自 C/C++。 我以前不知道这个。简洁的语言功能! @MathiasLykkegaardLorenzen 是的【参考方案7】:以与您可以在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
可以使代码保持美观和紧凑。
【讨论】:
WTF?如果您要标记 NEG,请大胆说出原因。 不是反对者,我认为与using
的比较是相当好的,尽管语义相当不同(这可能是它被反对的原因)。请注意,if (true)
是多余的。删除它,你就有了一个范围块。【参考方案8】:
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/。
【讨论】:
另外,有趣的是,当您进行更改时,您的示例代码将编译int y = this.x;
@Phil:正确。 this.x
不是一个简单的名字。【参考方案9】:
Kommer 的回答在技术上是正确的。让我用一个生动的盲屏隐喻来解释它。
for-block 和封闭的外部块之间有一个单向盲屏,这样 for-block 内的代码可以看到外部代码,但外部块中的代码看不到内部代码。
由于外部代码看不到内部,它不能使用内部声明的任何内容。但是由于 for 块中的代码可以看到 inside 和 outside ,因此在这两个地方声明的变量不能通过名称明确使用。
所以要么你看不到它,要么你 C#!
【讨论】:
以上是关于for循环中声明的变量是局部变量吗?的主要内容,如果未能解决你的问题,请参考以下文章