C# 8.0本质论
上QQ阅读APP看书,第一时间看更新

4.9 跳转语句

循环的执行路径可以改变。事实上,可用跳转语句退出循环,或者跳过一次循环迭代的剩余部分并开始下一次迭代——即使循环条件当前仍然为true。本节介绍让执行路径从一个位置跳转到另一个位置的几种方式。

4.9.1 break语句

C#使用break语句退出循环或switch语句。任何时候遇到break语句,控制都会立即离开循环或switch。代码清单4.50演示了井字棋程序的foreach循环。

代码清单4.50 发现赢家就用break跳出循环

输出4.24展示了结果。

输出4.24

代码清单4.50发现有玩家取胜后就执行break语句。break强迫它所在的循环(或switch语句)终止,控制转移到循环(或switch语句)后的下一个语句。在本例中,如果位比较返回true(当前棋盘上已有玩家取胜)就执行break语句,跳出当前foreach循环并显示赢家。

初学者主题:用按位操作符处理棋子分布

完整井字棋代码清单使用按位操作符判断哪个玩家取胜。首先,代码将每个玩家的落子位置保存到名为playerPositions的位映射中(用一个数组保存两个玩家的位置)。

最开始,playerPositions的两个位置都是0。玩家每次走棋,与落子位置对应的位都设为1。例如,假定玩家选择在单元格3落子,则shifter设为3 – 1。减1是因为C#数组基于0,应将0而非1视为第一个位置。接着用移位操作000000000000001 << shifter设置position,即与单元格3对应的位。其中shifter的当前值是2。最后将当前玩家的playerPositions设为0000000000000100(因为0基,所以还是要减1)。代码清单4.51使用|=合并之前和当前走棋。

代码清单4.51 设置与玩家每次走棋对应的位

之后就可迭代与棋盘上的取胜布局对应的每一个掩码,判断当前玩家是否得到了一个取胜布局,就像代码清单4.50展示的那样。

4.9.2 continue语句

循环主体可能有很多语句。如果想在符合特定条件时中断当前迭代,放弃执行剩余语句,可以使用continue语句跳到当前迭代的末尾,并开始下一次迭代。C#的continue语句允许退出当前迭代(无论剩下多少语句没有执行)并跳到循环条件。如循环条件仍为true,循环继续。

代码清单4.52使用continue语句只显示电子邮件地址的域部分。输出4.25展示了结果。

代码清单4.52 判断电子邮件地址的域

输出4.25

在代码清单4.52中,在遇到电子邮件地址的域部分之前,需一直使用continue语句来跳至电子邮件地址的下一个字符。

一般都可以用if语句代替continue语句,这样还能增强可读性。continue语句的问题在于,它在一次迭代中提供了多个控制流程,从而影响了可读性。代码清单4.53重写上面的例子,将continue语句替换成if/else构造来改善可读性。

代码清单4.53 将continue替换成if语句

4.9.3 goto语句

早期编程语言不像C#这些现代语言那样具备完善的“结构化”控制流程,它们要依赖简单的条件分支(if)和无条件分支(goto)语句来满足控制流程的需求。这样得到的程序难以理解。许多资深程序员觉得goto语句在C#中继续存在很反常。但C#确实支持goto,而且只能利用goto在switch语句中实现贯穿(直通)。在代码清单4.54中,如果设置了/out选项,就使用goto语句跳转到default,/f选项的处理与此相似。

代码清单4.54 演示带goto的switch语句

输出4.26演示了如何执行代码清单4.54的代码。

输出4.26

要跳转到标签不是default的其他switch小节,可以使用goto case constant;语法;其中constant是在目标标签中指定的常量。要跳转到没有和switch小节关联的语句,在目标语句前添加标识符和冒号,并在goto语句中使用该标识符。例如,可以写标签语句myLabel : Console.WriteLine();,然后用goto myLabel;跳到那里。幸好,C#禁止通过goto跳入代码块。只能用goto在代码块内部跳转或跳到一个包围当前块的代码块。通过这个限制,C#避免了在其他语言中可能遇到的大多数滥用goto的情况。

一般认为使用goto是不“优雅”的,不仅使代码难以理解,而且会令结构变差。要多次或者在不同情况下执行某个代码小节,要么使用循环,要么将代码重构为方法。

设计规范

·避免使用goto。