为什么我们不能在while循环中定义变量?
我们可以做的:
using (Stream s ..)
和:
for (int i ...)
为什么我们不能这样做:
while ((int i = NextNum()) > 0) {..}
我发现它非常有用和明智。
我不是语言设计师,但我会给它一个有根据的猜测。
每次执行循环时都会执行while()
内的子句。 (最后+1次。)语句int i = NextNum()
声明一个局部变量。 您不能多次声明局部变量。
更新
从语义上讲,这应该是可行的。 事实上,正如评论中所指出的,这在其他语言中是可能的。 但是,如果不重写一些主要的语法规则,这在C#中是不可能的。
必须在语句中声明局部变量。 我相信这种语言是这样分开的,因为变量声明并没有真正执行。 当您看到一行代码创建变量并为其赋值时,这实际上只是两个语句的快捷方式。 来自ECMA-334规范 :
这个例子
void F() { int x = 1, y, z = x * 2; }
完全对应于
void F() { int x; x = 1; int y; int z; z = x * 2; }
变量声明部分本身不是“执行”的。 它只是意味着应该在堆栈上为某种类型的变量分配一些内存。
while
语句期望一个布尔表达式,但表达式不能由语句组成 – 没有特殊的大小写一些新语法。
for
循环专门用于声明局部变量,但是你会注意到声明部分只被“执行”一次。 using
语句专门用于声明局部变量(并处理它们)。 它也只被“执行”一次。
还要考虑局部变量声明不返回值 – 它不能,因为它允许您声明多个变量。 这个陈述会返回哪个值?
int x = 1, y, z = x * 2;
上述语句是一个局部变量声明 。 它由一个类型和三个局部变量声明符组成 。 其中每一个都可以选择包含一个“=”标记和一个局部变量初始化器 。允许以这种方式声明局部变量意味着你需要稍微拆分现有的语法,因为你需要类型说明符,但是强制要求一个声明者,以便它可以返回一个值。
启用此行为也可能会产生负面影响,请考虑while
和do
/ while
语句相反,但是并行。 作为语言设计者,您是否还要启用do
语句来声明局部变量? 我不认为这是可能的。 您将无法在循环体中使用该变量,因为它尚未初始化(从第一次运行开始)。 只有while语句才有可能,但是你会破坏while
和do
语句之间的并行性。
不确定,但这是我受过教育的猜测。
for case有效,因为它实际上有3个部分。
- 迭代变量
- 终止条件
- 增量
这3次运行的次数不同。 #1只运行一次,#2运行迭代次数+1和#3每次运行运行一次。 因为#1只运行一次,所以它是一个定义变量的好地方。
现在让我们来看看while循环。 它只有1个部分,它每次迭代运行+ 1.由于它每次都运行,因此它不是定义变量的好地方,变量必须是条件的一部分。 它提出了类似的问题
- 定义是每次迭代发生一次还是一次? 如果是后者,有多少人会误解这个并搞砸了条件? 如果它是前者那么你有一个完整的声明,其中每个查询只执行一次部分
- 多变量定义如何表现?
我猜测复杂性/含糊不清是其不被允许的原因之一。 在优先级列表中,它可能永远不会很高。
与’for’循环相反,’while’没有初始化部分。 ‘for’语法如下所示:
for ( initializer; conditional expression; loop expression) { statements to be executed }
并且’while’看起来像这样:
while (condition) { statements to be executed }
与您的请求最接近的是:
int i; while ((i = NextNum()) > 0) { ... }
因为它在每次迭代中定义。 for循环不会这样做。 (所以你不能在第一个循环之后再次定义它。因为它已经存在。)
没有固有的原因导致它无法完成–C#设计师只是选择不允许它。 它在Perl中运行得很好,例如(“while((my $ i = NextNum())> 0){…}”)。
变量声明是声明; while循环需要一个表达式(表达式可以是一个语句,但不是相反)。 另一方面, using
和for
是特殊的,以允许变量声明。
更重要的是,这种事情的范围还不清楚。 考虑:
int x; // ... y = (int x = 42) + x; // what is y?
或者更糟:
(int x = 42) + (int x = 42);
甚至:
(int x = 42, y = 24) // what is the value of this expression?
一旦你允许声明是表达式,你必须处理这些事情。 特别是,最后一个示例成为一个问题,因为很难消除expression-decla-as-a-statement或statement-declaration之间的歧义,因此表达式样式必须足够通用才能成为语句声明的方式。
这开始变得有点像一个毛茸茸的设计挑战,因为它不是一个非常重要的特性,很可能C#委员会决定不允许声明是表达式(或者他们根本就没想过:)。
最后,你只需要一个for
循环就可以得到你想要的东西。 例如:
while ((int i = NextNum()) > 0) {..} // becomes... for (int i; (i = NextNum()) > 0; ) {..} // or... for (int i = NextNum(); i > 0; i = NextNum()) {..}