3.5 运算符
运算符是具有运算功能的符号。根据使用运算符的个数,可以将运算符分为单目运算符、双目运算符和三目运算符,其中:单目运算符是作用在一个操作数上的运算符,如正号(+)等;双目运算符是作用在两个操作数上的运算符,如加法(+)、乘法(*)等;三目运算符是作用在3 个操作数上的运算符,C# 中唯一的三目运算符就是条件运算符(?:)。本节将详细讲解C# 中的运算符。
3.5.1 算术运算符

C#中的算术运算符是双目运算符,主要包括+、-、*、/和% 5种,它们分别用于进行加、减、乘、除和模(求余数)运算。C# 中的算术运算符的功能及使用方式如表3.5 所示。
表3.5 C# 中的算术运算符的功能及使用方式

示例5.计算学生成绩的分数之差及平均分
某学员的3 门课的成绩:C 语言为89 分、C# 为90 分、SQL 为60 分,编程实现如下计算。
(1)C# 课和SQL 课的分数之差。
(2)3 门课的平均分。
代码如下。

计算学生成绩的分数之差及平均分代码的运行结果如图3.15 所示。

图3.15 计算学生成绩的分数之差及平均分代码的运行结果
学习笔记
在使用除法运算符和模运算符时,除数不能为0,否则将会出现异常,如图3.16所示。

图3.16 除数为0 时出现的异常
3.5.2 自增运算符和自减运算符

在使用算术运算符时,如果需要对数值型变量的值进行加1 或减1 操作,则可以使用下面的代码。

针对以上功能,C# 中还提供了另外的实现方式:自增运算符、自减运算符,它们分别用++ 和-- 表示,下面分别对它们进行讲解。
自增运算符和自减运算符是单目运算符,在使用时有两种形式,分别是++expr、expr++,或者--expr、expr--。其中,++expr、--expr 是前置形式,它表示expr 自身先加1或减1,其运算结果是自身修改后的值,再参与其他运算。而expr++、expr-- 是后置形式,它也表示自身加1 或减1,但其运算结果是自身未修改的值。也就是说,expr++、expr--是先参加完其他运算,然后进行自身加1 或减1 操作。自增运算符和自减运算符放在不同位置时的运算示意图如图3.17 所示。

图3.17 自增运算符和自减运算符放在不同位置时的运算示意图
例如,下面代码演示自增运算符放在变量的不同位置时的运算结果。

学习笔记
自增运算符、自减运算符只能作用于变量,因此,下面的形式是不合法的。

学习笔记
如果程序中不需要使用操作数原来的值,只是需要其自身进行加(减)1,那么建议使用前置自加(减),因为后置自加(减)必须先保存原来的值,而前置自加(减)不需要保存原来的值。
3.5.3 赋值运算符

赋值运算符主要用来为变量等赋值,它是双目运算符。C# 中的赋值运算符分为简单赋值运算符和复合赋值运算符,下面分别对其进行讲解。
1.简单赋值运算符
简单赋值运算符以符号“=”表示,其功能是将右操作数所含的值赋给左操作数。例如:

2.复合赋值运算符
在程序中对某个对象进行某种操作后,如果要将操作结果重新赋值给该对象,则可以通过下面的代码实现。

上面的代码看起来很烦琐,在C# 中,上面的代码等价于:


上面代码中的+= 就是一种复合赋值运算符。复合赋值运算符又称为带运算的赋值运算符,它其实是将赋值运算符与其他运算符合并成一个运算符来使用,从而同时实现两种运算符的效果的。
C# 提供了很多复合赋值运算符,其说明及运算规则如表3.6 所示。
表3.6 复合赋值运算符的说明及运算规则

3.复合赋值运算符的优势及劣势
在使用复合赋值运算符时,虽然 “a += 1” 与 “a = a + 1” 两者的运算结果是相同的,但是在不同的场景下,两种使用方法都有各自的优势和劣势,下面分别对其进行介绍。
1)低精度类型自增
在C# 中,因为整数的默认类型为int 型,所以下面的代码会报错。

在上面的代码中,在没有进行强制类型转换的条件下,a+1 的结果是一个int 型的值,无法直接赋给一个byte 型变量。但是如果使用“+=”实现递增计算,就不会出现这种问题,代码如下。

2)不规则的多值运算
复合赋值运算符虽然简洁、强大,但是有些时候是不推荐使用的。例如,下面的代码。

上面的代码如果改用复合赋值运算符实现,就会显得非常烦琐,代码如下。

学习笔记
在C# 中可以把赋值运算符连在一起使用。例如下面的代码,在这段代码中,变量x、y、z 都得到同样的值5,但在程序开发中不建议使用这种赋值语法。

3.5.4 关系运算符

关系运算符是双目运算符,它用于程序中的变量之间及其他类型的对象之间的比较,它返回一个代表运算结果的布尔值。当关系运算符对应的关系成立时,运算结果为true;否则为false。关系运算符通常用在条件语句中来作为判断的依据。C# 中的关系运算符有6 个,其作用及说明如表3.7 所示。
表3.7 C# 中的关系运算符的作用及说明

学习笔记
不等于运算符(!=)是与等于运算符相反的运算符,它与!(a==b) 是等效的。
示例6.使用关系运算符比较大小关系
创建一个控制台应用程序,声明3 个int 类型的变量,并分别对它们进行初始化,然后分别使用C# 中的各种关系运算符对它们的大小关系进行比较,代码如下。

学习笔记
第5 行代码使用Console.WriteLine(); 输出了一个空行,起到换行的作用。
第7 ~12 行代码主要演示6 种关系运算符的使用方法。
使用关系运算符比较大小关系代码的运行结果如图3.18 所示。

图3.18 使用关系运算符比较大小关系代码的运行结果
3.5.5 逻辑运算符

假定某面包店在每周二的下午7 点至8 点和每周六的下午5 点至6 点对生日蛋糕商品进行折扣让利活动,那么想参加折扣活动的顾客就要在时间上满足这样的条件:周二并且7:00 PM—8:00PM 或周六并且5:00 PM—6:00PM,这里就用到了逻辑关系,C# 中也提供了这样的逻辑运算符来进行逻辑运算。
逻辑运算符是对真和假这两种布尔值进行运算的,运算后的结果仍是一个布尔值。C#中的逻辑运算符主要包括&/&&(逻辑与)、|/||(逻辑或)、!(逻辑非)。在逻辑运算符中,除 “!” 是单目运算符外,其他都是双目运算符。表3.8 列出了逻辑运算符的用法和说明。
表3.8 逻辑运算符的用法和说明

在使用逻辑运算符进行逻辑运算时,其运算结果如表3.9 所示。
表3.9 使用逻辑运算符进行逻辑运算时的运算结果

学习笔记
逻辑运算符 “&&” 与 “&” 都表示 “逻辑与”,那么它们之间的区别是什么呢?从表3.9 中可以看出,当两个表达式都为true 时,逻辑与的结果才会为true。使用 “&” 会判断两个表达式;而 “&&” 则是针对bool 类型的数据进行判断的,当第一个表达式为false 时,则不去判断第二个表达式,直接输出结果,从而节省计算机判断的次数。通常将这种在逻辑表达式中根据左端的表达式可推断出整个表达式的值称为 “短路”,而那些始终执行逻辑运算符两边的表达式称为“非短路”。“&&”属于“短路”运算符,而 “&” 则属于 “非短路” 运算符。“||” 与 “|” 的区别跟 “&&” 与 “&” 的区别类似。
示例7.参加面包店的打折活动
创建一个控制台应用程序,使用代码实现本小节开始描述的场景,代码如下。


学习笔记
第9 行和第13 行代码使用了if…else 条件判断语句,该语句主要用来判断是否满足某种条件。该语句将在第4章进行详细讲解,这里只需要了解即可。
第9 行代码中在对条件进行判断时,使用了逻辑运算符&&、|| 和关系运算符==、>=、<=。
运行参加面包店的打折活动代码,符合条件的运行结果如图3.19 所示,不符合条件的运行结果如图3.20 所示。

图3.19 符合条件的运行结果

图3.20 不符合条件的运行结果
3.5.6 位运算符

位运算符的操作数类型是整型,可以是有符号的,也可以是无符号的。C# 中的位运算符有位与、位或、位异或、取反等运算符。其中,位与、位或、位异或为双目运算符,取反为单目运算符。位运算是完全针对位方面的操作,因此,它在实际使用时,需要先将要执行运算的数据转换为二进制,然后才能执行运算。
学习笔记
整型数据在内存中以二进制的形式表示,如整型变量7 的32 位二进制表示是00000000 00000000 00000000 00000111,其中,左边最高位是符号位,最高位是0 表示正数;若为1,则表示负数。负数采用补码表示,如-8 的32 位二进制表示为11111111 11111111 11111111 11111000。
1.位与运算
位与运算的运算符为 “&”。位与运算的运算法则是,如果两个整型数据a、b 对应位都是1,则结果位才是1;否则为0。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图3.21 所示。
2.位或运算
位或运算的运算符为 “|”。位或运算的运算法则是,如果两个操作数对应位都是0,则结果位才是0;否则为1。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图3.22 所示。

图3.21 12&8 的运算过程

图3.22 4|8 的运算过程
3.位异或运算
位异或运算的运算符是 “^”。位异或运算的运算法则是,当两个操作数的二进制表示相同(同时为0 或同时为1)时,结果为0;否则为1。若两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图3.23 所示。
4.取反运算
取反运算也称为按位非运算,运算符为 “~”。取反运算就是将操作数对应二进制中的1 修改为0,将0 修改为1,如图3.24 所示。

图3.23 31^22 的运算过程

图3.24 ~123 的运算过程
在C# 中使用Console.WriteLine 输出图3.21 ~图3.24 的运算结果,主要代码如下。

图3.21 ~图3.24 的运算结果如图3.25 所示。

图3.25 图3.21 ~图3.24 的运算结果
3.5.7 移位运算符

C# 中的移位运算符有两个,分别是左移位运算符<< 和右移位运算符>>,这两个运算符都是双目运算符,它们主要用来对整数类型数据进行移位操作。移位运算符的右操作数不可以是负数,并且要小于左操作数的位数。下面分别对左移位运算符<< 和右移位运算符>> 进行讲解。
1.左移位运算符<<
左移位运算符<< 是将一个二进制操作数向左移动指定的位数,左边(高位端)溢出的位被丢弃,右边(低位端)的空位用0 补充。左移位运算相当于乘以2 的n 次幂。
例如,int 类型数据48 对应的二进制数为00110000,将其左移1 位,根据左移位运算符的运算规则可以得出(00110000<<1)=01100000,所以转换为十进制数就是96(48×2);将其左移2 位,根据左移位运算符的运算规则可以得出(00110000<<2)=11000000,所以转换为十进制数就是192(48×22),其执行过程如图3.26 所示。

图3.26 左移位运算执行过程
2.右移位运算符>>
右移位运算符>> 是将一个二进制操作数向右移动指定的位数,右边(低位端)溢出的位被丢弃,而在填充左边(高位端)的空位时,如果最高位是0(正数),则左侧空位填入0;如果最高位是1(负数),则左侧空位填入1。右移位运算相当于除以2 的n 次幂。
正数48 右移1 位的运算过程如图3.27 所示。
负数-80 右移2 位的运算过程如图3.28 所示。

图3.27 正数48 右移1 位的运算过程

图3.28 负数-80 右移2 位的运算过程
学习笔记
由于移位运算的速度很快,在程序中遇到表达式乘以(或除以)2 的n 次幂的情况时,一般采用移位运算来代替。
3.5.8 条件运算符

条件运算符用 “?:” 表示,它是C# 中唯一的三目运算符,该运算符需要3 个操作数,形式如下。

在条件运算符的表达形式中,表达式1 是一个布尔值,可以为真或假。如果表达式1为真,则返回表达式2 的运算结果;如果表达式1 为假,则返回表达式3 的运算结果。例如:

学习笔记
条件运算符相当于一个if 语句,因此,上面的第2 行代码可以修改如下。

关于if 语句的详细讲解,请参看本书第4章。
另外,条件运算符的结合性是从右向左的,即从右向左运算。例如:

等价于:

示例8.使用条件运算符判断人的年龄所处阶段
创建一个控制台应用程序,使用条件运算符判断输入年龄所处的阶段,并输出相应的
提示信息,代码如下。

学习笔记
在第4 行代码中,Int32.Parse 方法用来将用户输入的年龄转换为int 类型,存储到int 类型变量中。
第6 行代码定义了一个string 类型的变量,记录条件表达式的返回结果。
使用条件运算符判断人的年龄所处阶段代码的运行结果如图3.29 所示。

图3.29 使用条件运算符判断人的年龄所处阶段代码的运行结果