何谓直达?在C(和C++)中,忽略break并且按以下编写代码是完全合法的:
switch (nVar)
{
case 1:
DoSomething();
case 2:
DoMore();
}
在这个例子中,在执行了第一个case语句的代码后,将直接执行到其它case标签的代码,直到一个break语句退出switch语段为止。尽管有时这是一个强大的功能,但它更经常地产生难于发现的缺陷。
可如果你想执行其它case标签的代码,那怎么办? 有一种办法,它显示于清单6.3中。
清单 6.3 在swtich语句中使用 goto 标签 和 goto default
2:
3: class SwitchApp
4: {
5: public static void Main()
6: {
7: Random objRandom = new Random();
8: double dRndNumber = objRandom.NextDouble();
9: int nRndNumber = (int)(dRndNumber * 10.0);
10:
11: switch (nRndNumber)
12: {
13: case 1:
14: //什么也不做
15: break;
16: case 2:
17: goto case 3;
18: case 3:
19: Console.WriteLine("Handler for 2 and 3");
20: break;
21: case 4:
22: goto default;
23: // everything beyond a goto will be warned as
24: // unreachable code
25: default:
26: Console.WriteLine("Random number {0}", nRndNumber);
27: }
28: }
29: }
在这个例子中,通过Random类产生用于控制表达式的值(第7~9行)。switch语段包含两个对switch语句有效的跳转语句。
goto case 标签:跳转到所说明的标签
goto default: 跳转到 default 标签
有了这两个跳转语句,你可以创建同C一样的功能,但是,直达不再是自动的。你必须明确地请求它。
不再使用直达功能的更深的含义为:你可任意排列标签,如把default标签放在其它所有标签的前面。为了说明它,我创建了一个例子,故意不结束循环:
{
default:
case 5:
goto default;
}
我已经保留了其中一个swich 语句功能的讨论直至结束——事实上你可以使用字符串作为常量表达式。这对于VB程序员,可能听起来不象是什么大的新闻,但来自C或C++的程序员将会喜欢这个新功能。
现在,一个 switch 语句可以如以下所示检查字符串常量了。
switch (strTest)
{
case "Chris":
Console.WriteLine("Hello Chris!");
break;
}
6.2 循环语句
当你想重复执行某些语句或语段时,依据当前不同的任务,C#提供4个不同的循环语句选择给你使用:
。for 语句
。foreach 语句
。while 语句
。do 语句
6.2.1 for 语句
当你预先知道一个内含语句应要执行多少次时,for 语句特别有用。当条件为真时,常规语法允许重复地执行内含语句(和循环表达式):
for (初始化;条件;循环) 内含语句
请注意,初始化、条件和循环都是可选的。如果忽略了条件,你就可以产生一个死循环,要用到跳转语句(break 或goto)才能退出。
{
break; // 由于某些原因
}
另外一个重点是,你可以同时加入多条由逗号隔开的语句到for循环的所有三个参数。例如,你可以初始化两个变量、拥有三个条件语句,并重复4个变量。
作为C或C++程序员,你必须了解仅有的一个变化:条件语句必须为布尔表达式,就象 if 语句一样。
清单6.4 包含使用 for 语句的一个例子。它显示了如何计算一个阶乘,比使用递归函数调用还要快。
清单 6.4 在for 循环里计算一个阶乘
2:
3: class Factorial
4: {
5: public static void Main(string[] args)
6: {
7: long nFactorial = 1;
8: long nComputeTo = Int64.Parse(args[0]);
9:
10: long nCurDig = 1;
11: for (nCurDig=1;nCurDig <= nComputeTo; nCurDig++)
12: nFactorial *= nCurDig;
13:
14: Console.WriteLine("{0}! is {1}",nComputeTo, nFactorial);
15: }
16: }
尽管该例子过于拖沓,但它作为如何使用for 语句的一个开端。
首先,我本应在初始化内部声明变量nCurDig:
for (long nCurDig=1;nCurDig <= nComputeTo; nCurDig++) nFactorial *= nCurDig;
另一种忽略初始化的选择如下行,因为第10行在for 语句的外部初始化了变量。(记住C#需要初始化变量):
for (;nCurDig <= nComputeTo; nCurDig++) nFactorial *= nCurDig;
另一种改变是把++操作符移到内含语句中:
for ( ;nCurDig <= nComputeTo; ) nFactorial *= nCurDig++;
如果我也想摆脱条件语句,全部要做的是增加一条if 语句,用break 语句中止循环:
{
if (nCurDig > nComputeTo) break;
nFactorial *= nCurDig++;
}
除了用于退出for语句的break语句外,你还可以用continue 跳过当前循环,并继续下一次循环。
{
if (5 == nCurDig) continue; // 这行跳过了余下的代码
nFactorial *= nCurDig++;
}
6.2.2 foreach 语句
已经在Visual Basic 语言中存在了很久的一个功能是,通过使用For Each 语句收集枚举。C#通过foreach 语句,也有一个用来收集枚举的命令:
foreach(表达式中的类型标识符) 内含语句
循环变量由类型和标识符声明,且表达式与收集相对应。循环变量代表循环正在为之运行的收集元素。
你应该知道不能赋一个新值给循环变量,也不能把它当作ref 或out 参数。这样引用在内含语句中被执行的代码。
你如何说出某些类支持foreach 语句? 简而言之,类必须支持具有 GetEnumerator()名字的方法,而且由其所返回的结构、类或者接口必须具有public 方法MoveNext() 和public 属性Current。如果你想知道更多,请阅读语言参考手册,它有很多关于这个话题的详细内容。
对于清单 6.5 中的例子,我恰好偶然选了一个类,实现了所有这些需要。我用它来列举被定义过的所有的环境变量。
清单 6.5 读所有的环境变量
2: using System.Collections;
3:
4: class EnvironmentDumpApp
5: {
6: public static void Main()
7: {
8: IDictionary envvars = Environment.GetEnvironmentVariables();
9: Console.WriteLine("There are {0} environment variables declared", envvars.Keys.Count);
10: foreach (String strKey in envvars.Keys)
11: {
12: Console.WriteLine("{0} = {1}",strKey, envvars[strKey].ToString());
13: }
14: }
15: }
对GetEnvironmentVariables的调用返回一个IDictionary类型接口,它是由.NET框架中的许多类实现了的字典接口。通过 IDictionary 接口,可以访问两个收集:Keys 和 Values。在这个例子里,我在foreach语句中使用Keys,接着查找基于当前key值的值(第12行)。
评论