C#还提供了另两个布尔操作符:逻辑AND(逻辑与)操作符(用&&表示)和逻辑OR(逻辑或)操作符(用||表示)。这两个操作符统称为条件逻辑操作符(conditional logical operator)。它们的作用是将两个布尔表达式或值合并成单独一个布尔结果。这两个二元操作符与相等/关系操作符的相似之处在于,它们的结果也是true或false。不同之处在于,它们操作的值(操作数)本身就必须是true或false。
只有在作为操作数的两个布尔表达式都为true的前提下,&&操作符的求值结果才为true。
例如,只有在percent的值大于或等于0,而且percent的值小于或等于100的前提下,以下语句才会将true值赋给validPercentage:
bool validPercentage;// 有效百分比
validPercentage = (percent >= 0) && (percent <= 100);
提示:新手常犯的一个错误是在合并两个测试的时候,只对percent变量命名一次,就像下面这样:
percent >= 0 && <= 100 // 这个语句不能编译
使用圆括号有助于避免这种类型的错误,同时也有助于澄清表达式。例如,可以对比一下以下两个表达式:
validPercentage = percent >= 0 && percent <= 100
validPercentage = (percent >= 0) &&(percent <= 100)
两个表达式返回的是同一个值,因为操作符&&的优先级低于 >=和<=。然而,第二个表达式采取更清晰易懂的方式表明了它的目的。
两个操作符任何一个为true,操作符||的求值结果都为true。使用操作符||,我们可以判断两个条件中是否有任何一个成立。在percent的值小于0或大于100的情况下,以下语句会将值true赋给invalidPercentage:
bool invalidPercentage;
invalidPercentage = (percent < 0) || (percent > 100);
4.2.3 短路求值操作符&&和||都支持一个名为短路求值(short circuiting)的功能。有时,根本没有必要将两个操作数都求值出来。例如,假定操作符&&的左操作数求值为false,那么整个表达式的结果肯定是false,无论右操作数的值是什么。类似地,如果操作符||的左操作数求值为true,那么整个表达式的结果肯定是true。在这些情况下,操作符&&和||将跳过对右侧的布尔表达式的求值。下面是一些例子:
(percent >= 0) && (percent <= 100)
在这个表达式中,假如percent的值小于0,那么操作符&&左侧的布尔表达式会被求值为false。该值意味着整个表达式的结果肯定为false,无论右侧的表达式是什么。所以,不会对右侧的表达式进行求值。再如下例:
(percent < 0) || (percent > 100)
在这个表达式中,假如percent的值小于0,那么操作符||左侧的布尔表达式会被求值为true。该值意味着整个表达式的结果肯定为true。所以,不会对右侧的表达式进行求值。
如果能小心地设计使用了条件逻辑操作符的表达式,就可以通过避免不必要的工作来提升代码的性能。将容易计算、简单的布尔表达式放到条件逻辑操作符的左边,将较复杂的表达式放到右边。在许多情况下,程序并不需要对更复杂的表达式进行求值。
4.2.4 操作符的优先级和结合性总结下表总结了迄今为止学过的所有操作符的优先级和结合性。同一个类别中的操作符具有相同的优先级。在“类别”列表中,各个类别按照从高到低的顺序排列。一个更高类别中的操作符优先于较低类别中的操作符。
类 别 |
操 作 符 |
描 述 |
结 合 性 |
主要(Primary) |
()
++
-- |
覆盖优先级
后递增
后递减 |
左 |
一元(Unary) |
!
+
-
++
-- |
逻辑NOT
加
减
前递增
前递减 |
左 |
乘(Multiplicative) |
*
/
% |
乘
除
求余 |
左 |
加(Additive) |
+
- |
加
减 |
左 |
关系(Relational) |
<
<=
>
>= |
小于
小于或等于
大于
大于或等于 |
左 |
相等(Equality) |
==
!= |
等于
不等于 |
左 |
条件AND(Conditional AND) |
&& |
逻辑AND |
左 |
条件OR(Conditional OR) |
|| |
逻辑OR |
左 |
赋值(Assignment) |
= |
|
右 |
4.3 使用if语句来做出决策 如果想根据一个布尔表达式的结果选择执行两个不同的代码块,就可以使用if语句。
4.3.1 理解if语句的语法 if语句的语法格式如下(if和else是C#的关键字):
if ( booleanExpression )
statement-1;
else
statement-2;
如果booleanExpression求值为true,就运行statement-1;否则就运行statement-2。else关键字和后续的statement-2是可有可无的。如果没有else子句,而且booleanExpression为false,那么什么事情都不会发生,程序将继续执行if语句之后的代码。
例如,以下if语句用于递增一个秒表的秒针(暂时忽略分钟)。如果seconds的值是59,就重置为0;否则就用操作符++来递增:
int seconds;
...
if (seconds == 59)
seconds = 0;
else
seconds++;
拜托,只用布尔表达式!
if语句中的表达式必须放在一对圆括号中。除此之外,表达式必须是布尔表达式。在另一些语言中(尤其是C和C++),还可以使用一个整数表达式,编译器能自动将整数值转换成true(非0值)或false(0)。C#不允许这样做。对于这样的表达式,编译器会报错。
如果在if语句中不慎写了一个赋值表达式,而不是执行一个相等性测试,C#编译器也能识别出这个错误。例如:
int seconds;
...
if (seconds = 59) // 编译时错误
...
if (seconds == 59) // 正确
在本来该用==的地方用了=,是C/C++程序容易出现bug的另一个原因。在C和C++中,会将所赋的值(59)悄悄地转换成一个布尔值(任何非0的值都会被视为true),造成每次都执行if语句之后的代码。
另外,布尔变量可以作为if语句的表达式来使用,但必须包含在一对圆括号中:
bool inWord;
...
if (inWord == true) // 可以这样写,但不常用
...
if (inWord) // 更好的写法
4.3.2 使用代码块来对语句进行分组在前面展示的if语法中,我们在if(booleanExpression)后面只指定了一个语句,在关键字else后面也只指定了一个语句。但是,我们经常需要在一个布尔表达式为true的前提下运行两个或者更多的语句。这时,可以将要运行的语句分组到一个新方法中,然后调用那个方法。但是,一个更简单的做法是将语句分组到一个代码块(block)中。代码块是指用一对大括号来封闭的一系列语句。代码块还界定了一个新的作用域。你可以在一个代码块内部定义变量,但这些变量会在代码块结束处消失。
在下例中,两个语句将seconds重置为0,并使minutes递增。我们将这两个语句划分到一个代码块中。假如seconds的值等于59,就执行整个代码块:
int seconds = 0;
int minutes = 0;
...
if (seconds == 59)
{
seconds = 0;
minutes++;
}
else
seconds++;
重要提示 假如省略大括号,将造成两个严重后果。首先,C#编译器只将第一个语句(seconds=0)与if语句关联起来,下一个语句(minutes++)不再成为if语句的一部分。其次,当编译器遇到else关键字时,不会将它与前一个if语句关联起来,所以会报告一个语法错误。
4.3.3 嵌套if语句可以在一个if语句中嵌套其他if语句。这样一来,就可以将一系列布尔表达式链接起来,它们将依次测试,直至其中一个表达式的值为true。在下例中,假如day值为0,则第一个测试的值为true,值"Sunday"将被赋给dayName变量。假如day值不为0,则第一个测试失败,控制将传递给else子句。该子句将运行第二个if语句,将day的值与1进行比较。注意,只有在第一个if测试为false的前提下,才会执行第二个if语句。类似地,只有在第一个if测试和第二个if测试为false的前提下,才会执行第三个if。
if (day == 0)
dayName = "Sunday";
else if (day == 1)
dayName = "Monday";
else if (day == 2)
dayName = "Tuesday";
else if (day == 3)
dayName = "Wednesday";
else if (day == 4)
dayName = "Thursday";
else if (day == 5)
dayName = "Friday";
else if (day == 6)
dayName = "Saturday";
else
dayName = "unknown";