本节课将介绍如何使用C#选择控制语句,第三课将达到如下几个目的:
1.学会"if"语句的用法。
2.学会"switch"语句的用法。
3.学会在"switch"语句中如何使用"break"语句。
4.理解"goto"语句的正确用法。
在前面几节课中,你所看到的程序都是顺序执行的。你无法控制输入语句,你所能做的就是跟着程序执行直到终止。本节课中,将介绍基于条件进行判断,从而选择进入相应的逻辑分支中去执行。
我们所介绍的第一个选择语句是"if"语句,它有三种基本形式:单条选择, 如果/否则,以及多情形选择。
| 1.清单3-1. IF语句的格式:IfSelection.cs |
| using System; class IfSelect { public static void Main() { string myInput; int myInt; Console.Write("Please enter a number: "); myInput = Console.ReadLine(); myInt = Int32.Parse(myInput); // Single Decision and Action with brackets if (myInt > 0) { Console.WriteLine("Your number {0} is greater than zero.", myInt); } // Single Decision and Action without brackets if (myInt < 0) Console.WriteLine("Your number {0} is less than zero.", myInt); // Either/Or Decision if (myInt != 0) { Console.WriteLine("Your number {0} is not equal to zero.", myInt); } else { Console.WriteLine("Your number {0} is equal to zero.", myInt); } // Multiple Case Decision if (myInt < 0 || myInt == 0) { Console.WriteLine("Your number {0} is less than or equal to zero.", myInt); } else if (myInt > 0 && myInt <= 10) { Console.WriteLine("Your number {0} is between 1 and 10.", myInt); } else if (myInt > 10 && myInt <= 20) { Console.WriteLine("Your number {0} is between 11 and 20.", myInt); } else if (myInt > 20 && myInt <= 30) { Console.WriteLine("Your number {0} is between 21 and 30.", myInt); } else { Console.WriteLine("Your number {0} is greater than 30.", myInt); } } } |
| 说明 |
1.清单3-1中的IF语句的各种格式都使用了同一个输入变量"myInt"。
这是从用户获得交互内容的另一种方式。我们首先输出一行信息:"Please enter a number:"到控制台。"Console.ReadLine()"语句使得程序等待来自用户的输入,一旦用户输入一个数字,按回车键之后,该数字以字符串的形式返回到"myInput"变量中,由于我们需要的是一个整数,所以需要转换变量"myInput"成整型数据。用命令"Int32.Parse(myInput)"即可完成。 (Int32 等数据类型将在后面的课程中加以介绍。) 转换结果放到"myInt"变量中,这是个整数类型。
2.有了我们所需要的类型的数据,就可以用"if"语句来进行条件判断了。
对于第一种形式的IF语句,格式为: if (boolean expression) { statements }。该语句必须以关键字"if"开始。之后,括号中为布尔表达式。该布尔表达式必须计算出一个true或者false值。在本例中,我们检查用户的输入,看看输入值是否大于0,如果表达式运算结果为true,就执行大括号中的语句。(我们把大括号之间的语句部分称为"block"。) 块中有一个或者多个语句。如果布尔表达式的值为false,我们就忽略块中的语句,直接执行块后面的语句。
3.除了没有块之外,第二种"if"语句的格式非常类似于第一种。
因此,如果布尔表达式为true,将会执行布尔表达式之后的第一条语句。当布尔表达式的值为false,布尔表达式之后的第一条语句将被忽略掉,而直接执行其后的程序语句。如果你只有一条语句需要执行,就用该格式的"if"语句。如果你打算在布尔表达式的值为true时,执行两条或者两条以上的语句,就必须把它们放到块中。我个人的建议是:无论需要执行几条语句,要养成把if语句放到块中的习惯, 这就使得你避免犯如下错误:当添加了一条语句之后,忘记了添加一对括号。
4.大多数时候,你需要作出如下选择:当条件满足时做一件事,否则做另外一件事。
清单3-1中,程序演示了这种if语句格式的用法。 当布尔表达式为true时, 就立刻执行"if"后面的那条语句, 而当布尔表达式为false时,就执行"else"关键字后面的语句。
5.当要计算多个布尔表达式时,你可以使用if/else if/else 这种格式,上面的例子程序演示了这种形式,从关键字"if"开始, 一旦布尔表达式为真,就执行if后面的块。但是,这一次,在组合关键字"else if"后面还可以进行多个条件的判断。"else if"语句后面也有个布尔表达式,一旦该布尔表达式的值为true,就会执行紧接其后的块。这种情形可以一直持续下去,直到所有的情况都已经计算出来,但是整个"if/else if"序列必须以"else"结束。当所有的"if"或者"else if" 后面的布尔表达式的值都为false时,就执行关键字"else"后面的块。 对于if/else if/else格式的语句,每次仅仅执行一个其中部分的语句 。
6.上面的例子中,布尔表达式 (myInt < 0 || myInt == 0)包含了条件OR (||)运算符。
对于常规OR (|)运算符和条件OR (||)运算符来说,只要有运算符两边的子表达式之一为真,整个布尔表达式的值就为真。两个运算符的区别在于:正规OR 运算符(|)每次都对运算符(|)两边的表达式进行计算。而条件运算符OR (||)只有当第一个子表达式的值为false时,才计算第二个子表达式的值。
7.布尔表达式 (myInt > 0 && myInt <= 10)包含了条件运算符AND。
对于常规AND (&) 运算符和条件AND (&&)运算符来说,只有游龙戏凤当运算符两边的子表达式的值都为真时,整个布尔表达式的值为真。两种运算符的区别在于:正规AND (&)运算符每次都计算运算符两边的子表达式的值,但是对于条件AND运算符来说,只有当第一个子表达式的值为真时,才计算第二个表达式的值。条件运算符(&& 和 ||) 通常称为运算优化的运算符,因为有时不需要计算整个表达式的值。这样就可以忽略掉不必要的逻辑表达式的计算,可以生成有效的代码。
同if/else if/else 格式的"if"语句类似,"switch"语句的用法如下:
| 2.清单3-2. 分支选择语句: SwitchSelection.cs |
| using System; class SwitchSelect { public static void Main() { string myInput; int myInt; begin: Console.Write("Please enter a number between 1 and 3: "); myInput = Console.ReadLine(); myInt = Int32.Parse(myInput); // switch with integer type switch (myInt) { case 1: Console.WriteLine("Your number is {0}.", myInt); break; case 2: Console.WriteLine("Your number is {0}.", myInt); break; case 3: Console.WriteLine("Your number is {0}.", myInt); break; default: Console.WriteLine("Your number {0} is not between 1 and 3.", myInt); } decide: Console.Write("Type \"continue\" to go on or \"quit\" to stop: "); myInput = Console.ReadLine(); // switch with string type switch (myInput) { case "continue": goto begin; case "quit": Console.WriteLine("Bye."); break; default: Console.WriteLine("Your input {0} is incorrect.", myInput); goto decide; } } } |
| 说明 |
| 右 | ||
| 逻辑与 | & | 左 |
| 逻辑异或 | ^ | 左 |
| 逻辑或 | | | 左 |
| 条件与 | && | 左 |
| 条件或 | || | 左 |
| 条件 | ?: | 右 |
| 赋值等 | = *= /= %= += -= <<= >>= &= ^= |= | 右 |
| 2.清单 1-2. 单目运算符: Unary.cs |
| using System; class Unary { public static void Main() { int unary = 0; int preIncrement; int preDecrement; int postIncrement; int postDecrement; int positive; int negative; sbyte bitNot; bool logNot; preIncrement = ++unary; Console.WriteLine("Pre-Increment: {0}", preIncrement); preDecrement = --unary; Console.WriteLine("Pre-Decrement: {0}", preDecrement); postDecrement = unary--; Console.WriteLine("Post-Decrement: {0}", postDecrement); postIncrement = unary++; Console.WriteLine("Post-Increment: {0}", postIncrement); Console.WriteLine("Final Value of Unary: {0}", unary); positive = -postIncrement; Console.WriteLine("Positive: {0}", positive); negative = +postIncrement; Console.WriteLine("Negative: {0}", negative); bitNot = 0; bitNot = (sbyte)(~bitNot); Console.WriteLine("Bitwise Not: {0}", bitNot); logNot = false; logNot = !logNot; Console.WriteLine("Logical Not: {0}", logNot); } } |
| 说明 |
1.当计算表达式的时候,在后置增一和后置减一运算符进行运算时,先返回其值,再进行增一或者减一运算。当使用前置加号和减号运算符进行运算时,是先进行增一或者减一的运算,然后再返回其结果值。
2.在清单1-2中, 变量unary初始化为0,进行++x 运算时,"unary"的值加1,再把其值1赋给"preIncrement"变量。在进行--x运算时,先把"unary"的值减到0, 再把值0赋给"preDecrement"变量。
3.进行x-运算时,先把"unary"的值0赋给"postDecrement" 变量,之后再把"unary"减到-1。进行x++运算时,先把"unary"的值-1赋给"postIncrement"变量,之后再对"unary"加1,使得"unary"变量现在的值为0。
4.变量"bitNot"初始值为0,进行按位取反运算,本例中,数0表示为二进制"00000000",按位取反之后变为-1,其二进制表示为"11111111"。
5.了解一下表达式"(sbyte)(~bitNot)", 任何对类型sbyte, byte, short 或者 ushort 类型数据的运算,返回结果都是整数。要把值赋给bitNot变量,我们必须使用cast (类型)运算符(强制类型转换),其中Type表示你希望转换成的类型(本例中为sbyte)。 Cast运算符把大范围类型的数据转换为小范围类型的数据时,须特别谨慎,因为此时有丢失数据的危险。一般来说,把小类型的数据赋给大类型变量,并没有问题, 因为大范围数据类型的变量具有足够的空间存放小类型数据。 注意在signed 和unsigned类型之间进行Cast运算时,也存在此类危险。 许多初级程序设计教程对变量的位表示作出了很好的讲解,同时也介绍了直接进行Cast运算的危险。
逻辑非(!)运算符可以处理布尔变量值。本例中,"logNot"变量从false 变为true。
| >Pre-Increment: 1 >Pre-Decrement 0 >Post-Decrement: 0 >Post-Increment -1 >Final Value of Unary: 0 >Positive: 1 >Netative: -1 >Bitwise Not: -1 >Logical Not: True |
| 3.清单 1-3. 二元运算符 Binary.cs |
| using System; class Binary { public static void Main() { int x, y, result; float floatResult; x = 7; y = 5; result = x+y; Console.WriteLine("x+y: {0}", result); result = x-y; Console.WriteLine("x-y: {0}", result); result = x*y; Console.WriteLine("x*y: {0}", result); result = x/y; Console.WriteLine("x/y: {0}", result); floatResult = (float)x/(float)y; Console.WriteLine("x/y: {0}", floatResult); result = x%y; Console.WriteLine("x%y: {0}", result); result += x; Console.WriteLine("result+=x: {0}", result); } } |
| 说明 |
清单1-3 演示了二元操作符的几个例子。加法(+),减法(-),乘法(*)和除法(/)的运算结果,就是我们通常进行的的四则运算的结果。
因为"floatResult"变量是浮点运算类型,所以整型变量"x"和"y" 被强制转换成浮点类型来计算FloatResult。
这里有个求余数的运算符,两个操作数相除,返回余数。
最后一条语句给出了另外一种赋值形式,这里用了(+=)运算符.无论什么时候你使用(+=)运算符,那么这个二进制运算符就应该在运算符左右两边都进行运算,然后把值赋给左边的参数。本语句相当于"result = result + x",并返回七磅同样的值。
前面的课程中,你看到的使用次数较多的一种类型是"string" (字符串)类型。"string"类型是由包含在引号内的Unicode编码的字符构成。例如"This is a string."
另外一种数据类型是数组。数组可以看成是同种类型的元素构成的集合。当声明数组时,你要指定类型名,数组名,维数和数组大小。
| 4.清单 1-4. Array Operations: Array.cs |
| using System; class Array { public static void Main() { int[] myInts = { 5, 10, 15 }; bool[][] myBools = new bool[2][]; myBools[0] = new bool[2]; myBools[1] = new bool[1]; double[,] myDoubles = new double[2, 2]; string[] myStrings = new string[3]; Console.WriteLine("myInts[0]: {0}, myInts[1]: {1}, myInts[2]: {2}", myInts[0], myInts[1], myInts[2]); myBools[0][0] = true; myBools[0][1] = false; myBools[1][0] = true; Console.WriteLine("myBools[0][0]: {0}, myBools[1][0]: {1}", myBools[0][0], myBools[1][0]); myDoubles[0, 0] = 3.147; myDoubles[0, 1] = 7.157; myDoubles[1, 1] = 2.117; myDoubles[1, 0] = 56.00138917; Console.WriteLine("myDoubles[0, 0]: {0}, myDoubles[1, 0]: {1}", myDoubles[0, 0], myDoubles[1, 0]); myStrings[0] = "Joe"; myStrings[1] = "Matt"; myStrings[2] = "Robert"; Console.WriteLine("myStrings[0]: {0}, myStrings[1]: {1}, myStrings[2]: {2}", myStrings[0], myStrings[1], myStrings[2]); } } |
| 说明 |
在本文开始写作的时候,虽然商用C# 编译器尚未推出, 但你可以下载微软的.NET Frameworks SDK Beta 1.
本节课通过介绍几个简单的程序,使得你对C#有所入门。本节程要达到如下几个目的:
1.理解一个C#程序的基本结构。
2.初步了解"名称空间"的概念。
3.初步了解"类"的概念。
4.了解"Main"方法所做的工作。
5.学会如何读取命令行输入信息。
6.学会使用控制台输入/输出 (I/O)语句。
| 1.清单1-1. 一个简单的欢迎程序Welcome.cs |
| // Namespace Declaration using System; // Program start class class WelcomeCSS { // Main begins program execution. public static void Main() { // Write to console Console.WriteLine("Welcome to the C# Station Tutorial!"); } } |
| 说明 |
1.清单1-1中的程序包括四个基本元素:名称空间的声明,类,"Main"方法和语句。
2.本例中对名称空间的声明,表明正在使用"System"这个名称空间。
名称空间内包含了一组可以被C#程序调用的代码。有了"using System;"这个声明,就表明程序可以引用该"System"名称空间内的代码,而无需在每个引用的前面加上"System"。关于这一点,我将在后面专门介绍名称空间的课程中详细介绍。
3.类"class WelcomeCSS"包含了程序所要用到的数据,和所要执行的方法的定义。
同诸如接口和结构这样的元素类似,类在程序中是用来描述对象的,这些元素都将会在后续课程中详细介绍。本例中的类不包含数据,只包含一个方法。该方法定义了该类的行为(或者称为该类所能做的事情)。
4.程序运行时,WelcomeCSS类中的该方法表明了该类所要完成的事情。
方法名"Main"作为保留字,作为程序的起点。"Main"前面是个名为"static"的修饰符。"static"修饰符表明该方法仅仅在该特定的类中工作,而不是在在该类的实例中工作。这是必需的,因为一旦程序启动后,并不存在对象的实例。类,对象和实例的具体用法将会在后面的课程中覆盖到。每个方法必须有个返回值类型。本例中,返回值类型是"void",它表明"Main"函数没有返回值。每个方法名的后面也都跟着个参数表,参数表包含有零个或者多个参数并用括号括起来。为了简单起见,没有在"Main"后面添加参数。后面的课程中,将介绍"Main"方法所允许采用的参数类型。
5."Main"方法通过"Console.WriteLine(...)" 语句表明其行为。
"Console" 是"System" 名称空间中的类。"WriteLine(...)"是"Console" 类中的方法。我们使用"."这个句点操作符来标记程序中的从属元素。注意到,我们也可以这样来书写:"System.Console.WriteLine(...)",这样的书写格式是很有趣的,它是根据"namespace.class.method" 的格式进行书写的。如果在程序的一开始,没有采用"using System"的声明,那么就必须严格遵守"System.Console.WriteLine(...)"这样的书写格式。该语句的执行结果是在控制台控制台上输出字符串"Welcome to the C# Station Tutorial!" 。
6.注释是由"//"标出的。
例子中的这些注释都是单行注释,表明从该注释符号的开始处到该行结束处,都是注释部分。如果你的注释要跨越若干行,即多行注释,可以以符号"/*"开始,以符号"*/"结束,其中所包含的全部是注释。你也可以在多行注释符号中包含单行注释。但是,不能在单行注释符号后面放上多行注释符号。程序编译时,将忽略掉注释部分。注释的目的是为了用简单的英语给程序所要完成的工作加上注解。
7.所有语句都以分号";"结束。
类和方法以"{"开始,以"}"结束。任何位于"{"和"}"之间的语句定义为块。块定义了程序元素的活动范围 (或者称为生命期和可见性),这些概念将在后面的课程中加以介绍。
8.可以编写出能够接受命令行输入信息的程序。
命令行输入信息的集合是在"Main"方法中进行处理的。清单1-2中的程序,可以从命令行中接受输入一个名字,之后在控制台上显示出来。
| 2.清单1-2. 读取命令行输入信息的程序NamedWelcome.cs |
| // Namespace Declaration using System; // Program start class class NamedWelcome { // Main begins program execution. public static void Main(string[] args) { // Write to console Console.WriteLine("Hello, {0}!", args[0]); Console.WriteLine("Welcome to the C# Station Tutorial!"); } } |
| 说明 |
1.记住,要把你的名字添加到命令行中。
例如,在命令行中打入"NamedWelcome Joe"。如果不这样做,程序就会崩溃,在后面的课程中,将介绍如何检测这种情况,以及如何避免这种情况的出现。
2.在清单1-2中,在"Main"方法的参数表中有个入口。
参数名是"args"。 在程序的后面部分就要引用该参数。"string[]"是参数"args"的类型。"string"类型用于存放字符。这些字符可以是一个单词,也可以是多个单词。方括号"[]"表示数组,"args"参数由命令行上的若干个单词构成。
3.在"Main"方法的语句中,多了一条"Console.WriteLine(...)"语句。
该语句中的参数表同以往的写法不同,其中有个格式字符串"{0}" 参数。 格式串中的第一个参数从数字0开始,第二个参数从数字1开始,依此类推。 "{0}" 参数意味着引号后面的参数值将会输出到该位置。现在让我们来看看引号后面的参数。
4."args[0]"参数,它指向"args"数组中的第一个字符串。
数组中的第一个元素是args[0], 第二个元素是args[1],依此类推。例如,如果我在命令行中写上"NamedWelcome Joe","args[0]"的值就为"Joe".
让我们回到在格式字符串中嵌入的"{0}" 参数吧,因为"args[0]"是格式串后面的第一个参数, 一旦执行该命令时,"args[0]"的值"Joe"就会替换掉格式串中的"{0}"。一旦执行命令:"NamedWelcome Joe",输出结果就会为:
| >Hello, Joe! >Welcome to the C# Station Tutorial! |
| 3.清单1-3. 交互式处理输入信息的程序 InteractiveWelcome.cs |
| // Namespace Declaration using System; // Program start class class NamedWelcome { // Main begins program execution. public static void Main() { // Write to console/get input Console.Write("What is your name?: "); Console.Write("Hello, {0}! ", Console.ReadLine()); Console.WriteLine("Welcome to the C# Station Tutorial!"); } } |
| 说明 |
| >What is your Name? >Hello, |