### 一、运算符与表达式
C#提供了大量的运算符,按需要操作数的数目来分,有一元运算符(如++)、二元运算符(如+,*)、三元运算符(如? : )。按运算功能来分,基本的运算符可以分为以下几类:
![1631424648343](C:\Users\86172\AppData\Roaming\Typora\typora-user-images\1631424648343.png)
### 二、算术运算符
算术运算符如表2.3所示,表中假设x,y是某一数值类型的变量,可以是整型也可以是浮点型。
| 运算符 | 含义 | 示 例 | 运算符 | 含义 | 示 例 |
| ------ | ---- | ---------------------- | ------ | ---- | ------------------------ |
| + | 加 | x + y ; x+3 ; | % | 取模 | x % y ; 11%3 ; 11.0 % 3; |
| - | 减 | x – y ; y-1 ; | ++ | 递增 | ++x ; x++ ; |
| * | 乘 | x * y ; 3*4 ; | - - | 递减 | - - x ; x- - ; |
| / | 除 | x / y ; 5/2; 5.0/2.0 ; | | | |
其中:
(1)“+-*/ ”运算与一般代数意义上及其他语言的相同,需要注意的是,当“/”作用的两个操作数都是整型时,其计算结果也是整型。
例如:
```
4/2 // 结果等于2
5/2 // 结果等于2
5/2.0 // 结果等于2.5
```
(2)“%”取模运算,即获得整数除法运算的余数,也称取余。
例如:
```
11%3 // 结果等于2
12%3 // 结果等于0
11.0%3 // 结果等于2,这与C/C++不同,在C#中,“%”也可作用于浮点类型的操作数
```
(3)“++”和“--”是一元运算符,作用的操作数必须是变量,而不能是常量或表达式。它们既可出现在操作数之前(前缀运算),也可出现于操作数之后(后缀运算)。
前缀和后缀有共同之处,也有很大区别,下面举例说明:
```
++x 先将x加1个单位,然后再将计算结果作为表达式的值
x++ 先将x的值作为表达式的值,然后再将x加1个单位
```
不管是前缀还是后缀,它们操作的结果对操作数而言,都是一样的(加了1个单位),但它们出现在表达式运算中是有区别的。
例如:
```
int x , y ;
x=5 ; y=++x ; // x和y的值都等于6
x=5 ; y=x++ ; // x的值是6,y的值是5
```
### 三、关系运算符
关系运算符用来比较两个操作数的值,如表 2.4 所示,假设 x,y 是某相应类型的操作数,运算结果为布尔型(true或false)。
| 运 算 符 | 操 作 | 结 果 |
| ------------ | ---------- | ------------------------------------- |
| > | x>y | 如果x大于y,则为true,否则为false |
| >= | x>=y | 如果x大于等于y,则为true,否则为false |
| < | x<y | 如果x小于y,则为true,否则为false |
| <= | x<=y | 如果x小于等于y,则为true,否则为false |
| = = | x= =y | 如果x等于y,则为true,否则为false |
| != | x!=y | 如果x不等于y,则为true,否则为false |
如果操作数是string类型的,则在下列两种情况下被视为两个string值相等:
① 两个值均为null。
② 两个值都是对字符串实例的非空引用,这两个字符串不仅长度相同,并且每一个对应的字符位置上的字符也相同。
例如:
```
int x=1 , y=1 ;
object b1 , b2 , b3;
string s1="ABCD", s2="1234" , s3 = "ABCD" ;
b1 = x ; b2 = b1; b3 = y ;
x = = y ; // 结果为true
b1 = =b2; // 结果为true
b1!=b3; // 结果为true
s1 = =s2; // 结果为false
s1 = =s3; // 结果为true
```
### 四、逻辑运算符
逻辑运算符是用来对两个布尔类型的操作数进行逻辑运算的,运算结果也是布尔类型,如表2.5所示。
| 运 算 符 | 含 义 | 运 算 符 | 含 义 |
| ------------ | ---------- | ------------ | ---------- |
| & | 逻辑与 | && | 短路与 |
| \| | 逻辑或 | \|\| | 短路或 |
| ^ | 逻辑异或 | ! | 逻辑非 |
假设p、q是两个布尔操作数,表2.6给出了它们逻辑运算的真值表。
| p | q | p & q | p \| q | p ^ q | ! p |
| ----- | ----- | ----- | ------ | ----- | ----- |
| true | true | true | true | false | false |
| true | false | false | true | true | false |
| false | true | false | true | true | true |
| false | flase | flase | false | false | true |
运算符“&&”和“||”的操作结果与“&”和“|”一样,但它们的短路特征使代码的效率更高。所谓短路,就是在运算过程中,如果计算第一个操作数时,就能得知运算结果,就不会再计算第二个操作数,如图2.6所示。
![1631424910342](C:\Users\86172\AppData\Roaming\Typora\typora-user-images\1631424910342.png)
**例如:**
```
int x , y ;
bool z ;
x = 1 ; y = 0 ;
z = ( x >1) & (++ y >0 ) // z的值为false,y的值为1
z = ( x >1) && (++ y >0 ) // z的值为false,y的值为0
```
### 五、位 运 算 符
位运算符如表 2.7 所示,借助它们可以完成对整型数的某一位的测试、设置,以及对一个数的位置进行移动等操作,这对许多系统级程序设计非常重要。
| 运 算 符 | 含 义 |
| ------------ | ---------- |
| & | 按位与 |
| \| | 按位或 |
| ^ | 按位异或 |
| ~ | 按位取反 |
| >> | 右移 |
| << | 左移 |
按位与、按位或、按位异或、按位取反运算与前面逻辑运算符的与、或、异或、非的操作含义相同,只不过位运算进一步把这种操作作用到每一个二进制位上,逻辑运算的真或假值对应位运算的位的1或0值。
例如:
![1631424960361](C:\Users\86172\AppData\Roaming\Typora\typora-user-images\1631424960361.png)
在实际使用中,按位与运算通常用于将某位置0或测试某位是0还是1;按位或运算通常用于将某位置1。
例如:
```
ushort n;
n=0x17ff ;
if ( n & 0x8000 == 0 )
Console.WriteLine ("最高位第十五位为0" ) ;
else
Console.WriteLine ("最高位第十五位为1" ) ;
n = n & 0x7ffff ; // n 的最高位(第15位)置0,其他位不变
n = n | 0x8000 ; // n 的最高位(第15位)置1,其他位不变
```
按位异或运算有一个特别的属性,假设有两个整型数x和 y,则表达式 (x ^
y) ^ y值还原为x,利用这个属性可以创建简单的加密程序。例如:
```
char ch1 = 'O' , ch2 = 'K' ;
int key = 0x1f ;
Console.WriteLine ("明文:" + ch1 + ch2 ) ;
ch1 = (char) (ch1 ^ key ) ;
ch2 = (char) (ch2 ^ key ) ;
Console.WriteLine ("密文:" + ch1 + ch2 ) ;
ch1 = (char) (ch1 ^ key ) ;
ch2 = (char) (ch2 ^ key ) ;
Console.WriteLine ("解码:" + ch1 + ch2 ) ;
```
移位运算符有两个:一个左移(<<),一个右移(>>)。
语法形式:
```
value << num_bits
value >> num_bits
```
(1)左移。将给定的value向左移动num_bits位,左边移出的位丢掉,右边空出的位填0。
例如:0x1A << 2,左移过程如图2.7(a)所示。
0x1A(十进制数 26)经过左移 2 位运算,结果值是 0x68(十进制数 104),相当于对0x1A的值乘以22。但如果左移丢掉的位含有1,那么左移之后的值可能反而会变小。
例如:0x4A << 2,左移过程如图2.7(b)所示。
![1631425032973](C:\Users\86172\AppData\Roaming\Typora\typora-user-images\1631425032973.png)
(2)右移。将给定的 value 向右移动 num_bits 位,右边移出的位丢掉,左边空出的位要根据value的情况填0或1。
若value是一个带符号数,则按符号(正数为0,负数为1)补位。
例如:0x77 >> 2,右移过程如图2.8(a)所示。0x8A >> 2,右移过程如图2.8(b)所示。
若 value 是一个无符号数,左边空出的位补0。右移运算符的作用相当于将value的值整除以
![1631425046164](C:\Users\86172\AppData\Roaming\Typora\typora-user-images\1631425046164.png)
例如:0x8AU >> 2,右移过程如图2.9所示。
![1631425058609](C:\Users\86172\AppData\Roaming\Typora\typora-user-images\1631425058609.png)
### 六、赋值运算符
#### 1.简单赋值运算符
运算符左边的称为左值,右边的称为右值。右值是一个与左值类型兼容的表达式(exp),它可以是常量、变量或一般表达式。左值必须是一个已定义的变量或对象(obj),因为赋值运算就是将表达式的值存放到左值,因此左值必须是内存中已分配的实际物理空间。
**语法形式:**
```
var = exp
```
#### 2.复合赋值运算符
在进行如x = x +3 运算时,C#提供了一种简化方式:x +=3,这就是复合赋值运算。
语法形式:
```
var op= exp // op 表示某一运算符
```
等价的意义是:
```
var = var op exp
```
除了关系运算符,一般二元运算符都可以和赋值运算符在一起构成复合赋值运算,如 表2.8所示。
| 运 算 符 | 用 法 示 例 | 等价表达式 | 运 算 符 | 用 法 示 例 | 等价表达式 |
| ------------ | ------------- | ---------- | ------------ | ------------- | ---------- |
| += | x += y | x = x + y | &= | x &= y | x = x & y |
| - = | x- = y | x = x-y | \|= | x \|= y | x = x \| y |
| *= | x *= y | x = x * y | ^= | x ^= y | x = x ^ y |
| /= | x /= y | x = x / y | >>= | x >>= y | x = x >> y |
| %= | x %= y | x = x % y | <<= | x <<= y | x = x << y |
### 七、条件运算符
语法形式:
```
exp1 ? exp2 : exp3
```
其中,表达式exp1的运算结果必须是一个布尔类型值,表达式exp2和exp3可以是任意数据类型,但它们返回的数据类型必须一致。
首先计算exp1的值,如果其值为true,则计算exp2值,这个值就是整个表达式的结果;否则,取exp3的值作为整个表达式的结果。
例如:
```
z = x > y ? x : y ; // z的值就是x,y中较大的一个
z = x >=0 ? x : x ; // z的值就是x的绝对值
```
### 八、运算符的优先级与结合性
当操作数出现在具有相同优先级的运算符之间时,如表达式“1062”按从左到右计算的结果是2,如果按从右到左计算,结果是6。“”运算符是按从左到右的次序计算的,也就是左结合;再如表达式“x=y=2”,它在执行时是按从右到左运算的,即先将数值2赋给变量y,再将y的值赋给x,所以“=”运算符是右结合的。
在表达式中,运算符的优先级和结合性控制着运算的执行顺序,也可以用圆括号“( )”显式地标明运算顺序,如表达式(x+y)* 2。
表2.9按照优先级从高到低的顺序列出了C#运算符的优先级与结合性。
| 类 别 | 运 算 符 | 结 合 性 |
| -------------- | ---------------------------------------------------- | ------------ |
| 初等项 | . ( ) [ ] new typeof checked unchecked | 从左到右 |
| 一元后缀 | ++ - - | 从右到左 |
| 一元前缀 | ++ - - + - ! ~ (T) (表达式) | 从右到左 |
| 乘法 | * / % | 从左到右 |
| 加法 | + - | 从左到右 |
| 移位 | << >> | 从左到右 |
| 关系和类型检测 | < > <= >= is as | 从左到右 |
| 相等 | = = != | 从左到右 |
| 逻辑与 | & | 从左到右 |
| 逻辑异或 | ^ | 从左到右 |
| 逻辑或 | \| | 从左到右 |
| 条件与 | && | 从左到右 |
| 条件或 | \|\| | 从左到右 |
| 条件 | ? : | 从右到左 |
| 赋值 | = *= /= %= += - = <<= >>= &= ^= \|= | 从右到左 |
#### 九、表达式中的类型转换
自动转换是通过使用C#的“类型提升规则”来完成的,下面是C#的类型提升规则。
(1)一个操作数是decimal类型,另一个操作数提升为decimal,但float或double类型不能自动提升为decimal类型。
(2)一个操作数是double类型,另一个操作数提升为double类型。
(3)一个操作数是float类型,另一个操作数提升为float类型。
(4)一个操作数是 ulong类型,另一个操作数提升为 ulong类型,但带符号数,如sbyte、short、int类型或long类型不能自动提升。
(5)一个操作数是long类型,另一个操作数提升为long类型。
(6)一个操作数是uint类型,另一个操作数若是sbyte、short或int类型,那么这两个操作数都提升为long类型。
(7)一个操作数是uint类型,另一个操作数提升为uint类型。
(8)除以上情况之外,两个数值类型的操作数都提升为int类型。
从上述的自动转换规则可以看出,并不是所有数据类型都能在同一个表达式中混合使用。例如,float类型就不能自动转换为decimal类型。但是,如果使用强制类型转换仍有可能获得不兼容数据类型之间的转换。
强制类型的转换形式:
```
(类型) 表达式
```
**例如:**
```
decimal d1, d2 ;
float f1;
d1 = 99.999;
f1 = 0.98;
d2 = d1 + f1 ; // 出错,因为float类型的f1不能自动转换成decimal类型
d2 = d1 + (decimal) f1; // 使用强制类型转换后,不再报错
```