Web应用开发技术与案例教程
上QQ阅读APP看书,第一时间看更新

4.4 流程控制

C#的流程控制语句与C基本相同。

在学习一种编程语言时,流程控制语句总是其中最重要的内容。但是当使用过几种编程语言之后,有经验的程序员大多会有这种感触:当初被当作难点的流程控制语句,其实是最简单的了,因为每种语言的这部分都非常相似。本书认为读者已经具有一定的C或C++语言基础,因此这部分内容只做简单介绍。

4.4.1 条件语句

1.if语句

if语句是条件选择语句,它通过判断给定的条件是否为真来决定所要执行的操作。if语句的一般形式如下。

978-7-111-63649-6-Chapter04-52.jpg

if语句的执行过程是:首先计算if后面括号内布尔表达式的值,如果它的值为真就执行语句1,如果为假就执行语句2。if语句中可以省略else和语句2部分,其形式为:

978-7-111-63649-6-Chapter04-53.jpg

2.switch语句

switch语句用于多分支选择,它的一般形式如下。

978-7-111-63649-6-Chapter04-54.jpg

switch语句中控制表达式的数据类型可以是sbyte、byte、short、ushort、int、uint、long、ulong、char、string或枚举类型。每个case标签中的常量表达式必须属于或能隐式转换成控制表达式类型。

switch语句的执行过程是:首先计算switch后面圆括号内控制表达式的值,然后依次与各个case标签后面的常量表达式的值相比较,若一致就执行该case标签后面的语句组,直到遇到break语句。如果有两个或两个以上case标签中的常量表达式的值相同,编译时将会报错。如果控制表达式的值与所有常量表达式的值都不相等,则转向default标签后面的语句组去执行,如果没有default部分,则不执行任何语句,而直接转到switch语句后面的语句执行。switch语句中最多只能有一个default标签。

C#的switch语句与C和C++有如下不同。

(1)C和C++允许switch语句中case标签后不出现break语句,但C#不允许这样,它要求每个标签项后必须有break语句或跳转语句goto,即不允许从一个case自动遍历到其他case,否则编译时将报错。如果想要像C和C++那样,执行完一个语句组后继续执行其他的语句,只需要显式地加入goto case label(跳至标签语句执行)或goto default(跳至default标签执行)语句即可。

(2)C#可以把字符串当成常量表达式来使用,所以switch语句的控制表达式类型可以是string类型。

4.4.2 循环语句

1.while循环语句

while循环语句的一般形式如下。

978-7-111-63649-6-Chapter04-55.jpg

该语句的执行过程是:先计算while后面圆括号内布尔表达式的值,如果布尔表达式的值为真,则执行后面的循环体语句,然后再次计算布尔表达式的值;重复上述过程,直到布尔表达式的值为假时退出循环。

2.do-while循环语句

do-while循环语句的一般形式如下。

978-7-111-63649-6-Chapter04-56.jpg

该语句的执行过程是:先执行循环体语句,再计算while后面圆括号内布尔表达式的值,如果其值为真,则再次执行循环体;如此重复,直到布尔表达式的值为假时退出循环。

3.for循环语句

for循环语句的一般形式如下。

978-7-111-63649-6-Chapter04-57.jpg

for后面括号中的三部分都是可选的,其中初始化和循环还可以由多个语句(用逗号隔开)组成。“初始化”一般是为循环变量赋初值,通常为赋值语句。“条件”是循环控制条件,为布尔表达式。“循环”是循环变量的修改部分,用来表达循环变量的增量,通常是赋值语句,常用自加、自减运算。语句部分为循环体,可以是一条语句,也可以是复合语句和空语句。

for语句的执行过程是:先执行初始化部分。再计算条件表达式的值,若该值为假,则退出循环;若为真,则执行循环体。然后执行“循环”部分,对循环变量进行修改后再计算条件表达式,若为真,再一次执行循环体。如此重复,直到条件表达式的值为假时退出循环。

4.foreach语句

foreach语句是C#新引入的,C和C++中没有这个语句。它表示收集一个集合中的所有元素,并针对每个元素执行一次循环体。foreach语句的格式为:

978-7-111-63649-6-Chapter04-58.jpg

其中类型(type)和标识符(identifier)用来声明循环变量,表达式(expression)对应集合。

每次循环,先从集合中取一个元素赋给循环变量,再执行一次循环体语句;当依次处理完集合中的所有元素后,退出循环。在循环体中,循环变量是一个只读型局部变量。如果试图改变它的值,或将它作为一个ref或out类型的参数传递,都将引发编译时错误。

foreach语句中的表达式结果必须是集合类型。如果该集合的元素类型与循环变量类型不一致,则必须有一个显式定义的从集合中的元素类型到循环变量类型的转换。

下面是一个在数组上使用foreach语句的例子。

978-7-111-63649-6-Chapter04-59.jpg

运行结果为:

978-7-111-63649-6-Chapter04-60.jpg

本例先初始化一个Fibonacci数列并放到数组中,然后使用foreach语句显示其中的每个元素。从本例可以看出,数组类型是支持foreach语句的,属于集合类型。对于一维数组,执行顺序是从下标为0的元素开始一直到数组的最后一个元素;对于多维数组,元素下标的递增从最右边那一维开始,依次类推。

C#中包括很多有用的集合类型,如数组、ArrayList、哈希表、字典、堆栈和队列等,本书不再对这些集合类型进行详细介绍。下面给出一个在哈希表上使用foreach语句的例子。

978-7-111-63649-6-Chapter04-61.jpg

978-7-111-63649-6-Chapter04-62.jpg

运行结果为:

978-7-111-63649-6-Chapter04-63.jpg

注意,要在程序中使用哈希表,需要在程序的开头处使用using关键字引用Collections命名空间。

978-7-111-63649-6-Chapter04-64.jpg

5.循环的退出

在上面介绍的4种循环语句中,都可以使用break语句退出循环,继续执行循环语句后面的语句。也可以用continue语句来停止本次循环体语句的执行,继续进行下一轮循环。

4.4.3 异常处理语句

在编写程序时,需要考虑到各类不可预期的事件,比如被0除、内存不够、磁盘出错、网络资源不可用和数据库无法访问等。C#具有完备的异常处理功能,提供了处理程序运行时出现的任何异常情况的方法。C#的异常处理机制与C++非常相似,异常可以在两种不同的方式下被引发。

● 在程序中使用throw语句,主动、即时地抛出异常。

● 语句和表达式执行过程中激发了某个异常的条件,使得操作无法正常结束,从而引发异常。

使用throw语句抛出一个异常,其格式为:

978-7-111-63649-6-Chapter04-65.jpg

异常处理使用try、catch和finally关键字来尝试可能引发异常的操作、处理失败,以及在事后清理资源,其一般形式为:

978-7-111-63649-6-Chapter04-66.jpg

978-7-111-63649-6-Chapter04-67.jpg

从上面的形式可以看出,异常处理分为3个部分,其中第2、3部分是可以省略的。

在C#中,程序中的运行时错误使用一种称为“异常传播”的机制在程序中传播。异常由遇到错误的代码引发,由能够更正错误的代码捕获。异常可由.NET Framework公共语言运行库(CLR)或由程序中的代码引发。一旦引发了一个异常,未捕获的异常由系统提供的通用异常处理程序处理,该处理程序会显示一个对话框。

程序执行“执行部分”,如果未发生异常,则不需要进行异常处理;如果发生异常,这个异常就会在调用堆栈中往上传播,直到找到针对它的catch子句。catch子句可以有多个,分别捕获并处理不同种类的异常类型。未捕获的异常由系统提供的通用异常处理程序处理,程序将停止执行,并显示一条错误信息。

无论是否引发了异常,finally块中的“必要处理”代码总会被执行,因此可以将用于释放资源的代码放在此处。

下面是一个异常处理的实例。

978-7-111-63649-6-Chapter04-68.jpg

这是一个求阶乘的例子,但其中的整数使用short类型,这样就增大了引起溢出的可能性。如果输入的值为3,则无溢出,程序的运行结果为:

978-7-111-63649-6-Chapter04-69.jpg

如果输入的值为10,则产生溢出,程序的运行结果为:

978-7-111-63649-6-Chapter04-70.jpg

还可能由于其他原因引发异常。上例中如果输入格式不正确,则不能转换为合法的数值,也会引发异常,例如:

978-7-111-63649-6-Chapter04-71.jpg

如果catch子句中指定了异常类型,则它必须是System.Exception类型或它的派生类型。如果同时指定了类型和标识符,就是声明了一个异常变量(如上例中的e)。异常变量相当于一个作用范围为整个catch块的局部变量。在catch块的执行过程中,异常变量描述了当前正在处理的异常。如果想引用异常对象(其中包括很多重要的错误信息),就必须定义异常变量。

checked和unchecked操作符用于整型算术运算时控制当前环境中的溢出检查。当算术运算产生一个目标类型无法表示的大数时,在使用了checked操作符的表达式中,会抛出溢出异常;而在使用了unchecked操作符的表达式中,则返回值被截掉不符合目标类型的高位,而不抛出异常。如果表达式没有包括任何checked或unchecked操作符,溢出时是否会抛出异常取决于外部因素,如编译器状态、执行环境参数等。而对于一个常量表达式而言,总是默认为进行溢出检查。使用了unchecked操作符后,溢出的发生不会导致编译错误,但往往会出现一些不可预期的结果,所以使用unchecked操作符要小心。

当try语句执行完以后,finally块中的语句必将被执行,无论是否会发生由以下原因导致的程序控制转移:普通操作;执行break、continue、goto或return语句;将异常传播到语句之外。