“要想成为真正的程序员,我们需要进行一场洗礼。”
“程序 = 数据结构 + 算法。”这样的公式很精辟,它越过了表层直接描述了程序的本质。不仅如此,这样几个简单的单词也让我们明白“我们应该学习什么内容?”。人们做任何事都有一定的程序,只是没有意识到。有些人将日常行动列成表格,这就是“编写程序”。
本章的学习重点:
◆ 运算符分类
◆ 运算符优先级
1.4运算符
表达式 (expression) 由操作数 (operand) 和运算符 (operator) 构成。表达式的运算符用来指示对操作数进行什么样的运算。常用的运算符包括 +、-、*、/ 和 %等。操作数由变量和常量构成。常量我们在后面的章节会向大家讲到,这里主要向大家介绍运算符的相关知识,希望对大家有所帮助。
1.4.1运算符分类
运算符是表达式中相当重要的一部分,它指示对表达式中的操作数进行什么样的运算,如+、–、*、/、%等。根据运算符操作的个数,可以把运算符分为以下3类:
一元运算符:只带有一个操作数并使用前缀表示法(如--x)或后缀表示法(如x++)。
二元运算符:带有两个操作数并且全都使用中缀表示法(如x+y)。
三元运算符:带有3个操作数并使用中缀表示法(如 ?: 运算符)。注意:C#语言只包含一个三元运算符。
下表列出了C#常见运算符:
类别 运算符
算术运算符 + – * / %
逻辑运算符 & | ^ ~ && || !
字符串连接运算符 +
增量和减量运算符 ++ – –
移位运算符 << >>
比较运算符 == != < > <= >=
赋值运算符 = += –= *= /= %= &= |= ^= <<= >>=
成员访问运算符 .
索引运算符 []
数据类型转换运算符 ()
条件运算符 ?:
委托连接和删除运算符 + –
对象创建运算符 New
类型信息运算符 sizeof (只用于不安全的代码) is typeof as
溢出异常控制运算符 checked unchecked
间接寻址运算符 * –> & (只用于不安全代码) []
命名空间别名限定符 ::
空接合运算符 ??
!、++x、--x和(T)x被称为一元运算符。其中,(T)x运算符为强制转换运算符,它可以将一种类型转换为另外一种类型。下面我们详细介绍!、++x、--x和(T)x运算符以及其他运算符的使用方法。
1、++运算符
++运算符又称为增量运算符,它既可以放到操作数的左边,也可以放到操作数的右边。如果++运算符放在操作数的左边,则称为该运算符为前缀增量运算符。否则,称为后缀增量运算符。语法如下:
++ expression或expression++
expression表示操作数。“++ expression”表达式首先计算expression的值,然后将其值增1,并作为该表达式的结果。“expression ++”表达式首先计算expression的值,并作为该表达式的结果,然后将expression的值增1。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestIncrement
05 {
06 public static void Main(string[] args)
07 {
08 int a = 2008; //定义一个a变量
09 int b = ++a; //进行前缀增量运算,并定义一个b变量保存结果
10 int c = a++; //进行后缀增量运算,并定义一个c变量保存结果
11 Console.WriteLine("a的值为:" + a); //输出结果
12 Console.WriteLine("b的值为:" + b); //输出结果
13 Console.WriteLine("c的值为:" + c); //输出结果
14 }
15 }
16 }
上述代码中,第9行“++a”表达式执行之后,a变量的值为2009,并作为该表达式的值。因此,b变量的值为等于2009。第10行“c = a++”表达式首先获取a变量的值,并赋值给c变量,因此,c变量的值为2009。然后,再将a变量的值增1。因此,a变量的值为2010。
最后的输出结果:
a的值为:2010
b的值为:2009
c的值为:2009
2、--运算符
--运算符又称为减量运算符,它既可以放到操作数的左边,也可以放到操作数的右边。如果--运算符放在操作数的左边,则称为该运算符为前缀减量运算符。否则,称为后缀减量运算符。语法如下:
-- expression或expression--
expression表示操作数。“-- expression”表达式首先计算expression的值,然后将其值减1,并作为该表达式的结果。“expression --”表达式首先计算expression的值,并作为该表达式的结果,然后将expression的值减1。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestDeduction
05 {
06 public static void Main(string[] args)
07 {
08 int a = 2008; //定义一个a变量
09 int b = --a; //进行前缀减量运算,并定义一个b变量保存结果
10 int c = a--; //进行后缀减量运算,并定义一个c变量保存结果
11 Console.WriteLine("a的值为:" + a); //输出结果
12 Console.WriteLine("b的值为:" + b); //输出结果
13 Console.WriteLine("c的值为:" + c); //输出结果
14 }
15 }
16 }
上述代码中,第9行“-- a”表达式执行之后,a变量的值为2007,并作为该表达式的值。因此,b变量的值为等于2007。第10行“c = a --”表达式首先获取a变量的值,并赋值给c变量,因此,c变量的值为2007。然后,再将a变量的值减1。因此,a变量的值为2006。
最后的输出结果:
a的值为:2006
b的值为:2007
c的值为:2007
3、!运算符
!运算符又称为逻辑否定运算符。如果操作数的值为true,则结果为false。如果操作数为false,则结果为true。语法如下:
! expression
expression表示操作数。只能使用布尔类型的数据,不能使用其它数据类型使用!运算符,负责编译会出现错误。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestNot
05 {
06 public static void Main(string[] args)
07 {
08 bool a = true; //定义一个a变量
09 bool b = !a; //进行逻辑否定运算,并定义一个b变量保存结果
10 Console.WriteLine("a的值为:" + a); //输出结果
11 Console.WriteLine("b的值为:" + b); //输出结果
12 }
13 }
14 }
上述代码中,第9行“!a”表达式执行之后,a变量的值为false,并作为表达式的值。因此,b变量的值为等于false。
最后的输出结果:
a的值为:True
b的值为:False
4、(T)x运算符
(T)x运算符将表达式显式转换为指定的类型,语法如下:
( type ) expression
type表示类型,expression表示被转换的表达式。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestConvertTypes
05 {
06 public static void Main(string[] args)
07 {
08 object o = 2009; //定义一个o变量
09 int i = (int)o; //进行显式转换
10 Console.WriteLine("i的值为:" + i);
11 }
12 }
13 }
上述代码中,第9行(int)o进行了显示类型转换,把o变量的值转换为int类型。注意:对于( T )x表达式而言,不存在从x的类型到T的显式转换,会发生编译错误的。
最后的输出结果:
i的值为:2009
5、算术运算符
*、/、%、+和–运算符称为算术运算符,它们分别表示乘法、除法、余数、加法和减法运算。语法如下:
left expression operator right expression
left expression和right expression分别表示左操作数和右操作数,operator表示运算符,可以为*、/、%、+和–。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestNumber
05 {
06 public static void Main(string[] args)
07 {
08 int a = 1; //定义a变量
09 int b = 1; //定义a变量
10 int c = a + b; //进行算术运算
11 Console.WriteLine("c的值为:" + c); //输出结果
12 }
13 }
14 }
上述代码中,a变量的值为1,b变量的值为1,执行+运算之后,c变量的值为等于2。如果左、右操作数的类型相同,则它们的运算结果的类型和操作数的类型相同。当运算结果超出了该类型表示的范围时,如果使用了checked运算符,则引发System.OverflowException异常。如果使用了unchecked运算符,则不引发异常,但是其结果类型范围外的任何有效高序位都被放弃。
最后的输出结果:
c的值为:2
对于+运算符而言,如果其一个或两个操作数为string类型时,则+运算符执行字符串串联操作,而不是简单的算术运算。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestString
05 {
06 public static void Main(string[] args)
07 {
08 string stra = "2008"; //定义一个stra变量
09 string strb = ".08.08"; //定义一个stra变量
10 string strc = stra + strb; //进行连接字符串运算
11 Console.WriteLine(strc); //输出结果
12 }
13 }
14 }
上述代码中,第8行和第9行分别定义了一个字符串变量stra和strb。然后我们使用+运算符进行运算,把运算结果保存在strc中。strc变量的值为2008.08.08字符串。
6、逻辑运算符
&、^和 | 运算符称为逻辑运算符。&运算符计算两个操作数的按位逻辑AND,| 运算符计算两个操作数的按位逻辑OR,^ 运算符计算两个操作数的按位逻辑XOR。语法如下:
left expression operator right expression
left expression和right expression分别表示左操作数和右操作数,operator表示运算符,可以为&、^和 |。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestLogic
05 {
06 public static void Main(string[] args)
07 {
08 bool a = true; //定义一个a变量
09 bool b = false; //定义一个a变量
10 bool c = a & b; //进行逻辑与运算
11 Console.WriteLine("c变量的值为" + c); //输出结果
12 }
13 }
14 }
上述代码中,第10行我们进行了逻辑运算,具体规则如下:
如果a和b均为true,则a & b的结果为true。否则,结果为false。
如果a或b为true,则a | b的结果为true。否则,结果为false。
如果a为true而b为false,或者a为false而b为true,则a ^ b的结果为true。否则,结果为false。即a和b的值不相同时,a ^ b的结果才为true。
最后的输出结果:
c变量的值为false。
7、条件运算符
?: 运算符称为条件运算符,它是C#语言中唯一的一个三元运算符。语法如下:
expression ? trueResult : falseResult;
该表达式首先计算条件表达式的值。如果条件表达式的值为真,则trueResult的值,并成为运算结果。否则计算falseResult,并成为运算结果。下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestCondition
05 {
06 public static void Main(string[] args)
07 {
08 int a = 10; //定义一个a变量
09 int b = 2008; //定义一个b变量
10 int c = a + b > 3000 ? a : b; //进行条件运算
11 Console.WriteLine("c变量的值为" + c); //输出结果
12 }
13 }
14 }
上述代码中,第10行如果a + b表达式的值大于3000时,则c的值等于a的值,否则c的值等于b的值。
最后的输出结果:
c变量的值为2008
8、条件逻辑运算符
&& 和 || 运算符称为条件逻辑运算符。&& 运算符为逻辑与运算符,它计算左右操作数的逻辑与。|| 运算符为逻辑或运算符,它计算左右操作数的逻辑或。语法如下:
left expression operator right expression
left expression和right expression分别表示左操作数和右操作数。operator表示运算符,可以为&&和||。
示例:使用&&运算符计算a和b两个操作数(均为bool类型)的和,结果保存为c变量。
bool a = true;
bool b = false;
bool c = a && b;
分析:c变量的值为false。
注意:对于a && b表达式而言,仅当a的值为true时,才计算b的值。对于a || b表达式而言,仅当a的值为false时,才计算b的值。
9、移位运算符
<<和>>运算符被称为移位运算符。<<运算符表示左移位,>>运算符表示右移位。语法如下:
expression operator count;
expression表示被移位的表达式,count表示移动的位数,operator表示运算符,可以为<<和>>。
下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestMove
05 {
06 public static void Main(string[] args)
07 {
08 int a = 2008; //定义一个a变量
09 int c1 = a << 2; //进行向左移位
10 int c2 = a >> 2; //进行向右移位
11 Console.WriteLine("c1变量的值为" + c1); //输出结果
12 Console.WriteLine("c2变量的值为" + c2); //输出结果
13 }
14 }
15 }
上述代码中,a << count表达式将a向左移位count个位。计算方法为:放弃a中经移位后的高序位(如果保留这些高序位,则结果将超出结果类型的范围),将其余的位向左位移,并将空出来的低序位均设置为零。a >> count表达式将a向右移位count个位。计算方法如下:
当a为int或long类型时,放弃a的低序位,将剩余的位向右位移。如果x非负,则将高序空位位置设置为零。如果a为负,则将其设置为1。
当a为uint或ulong类型时,放弃a的低序位,将剩余的位向右位移,并将高序空位位置设置为零。
最后的输出结果:
c1变量的值为8032
c2变量的值为502
10、关系和类型测试运算符
==、!=、<、>、<=、>=、is和as运算符称为关系和类型测试运算符。语法如下:
left expression operator right expression
left expression和right expression分别表示左操作数和右操作数,operator表示运算符,可以为==、!=、<、>、<=、>=、is和 as。其中,==、!=、<、>、<=和>=运算符为比较运算符,它们的具体计算方法如下:
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。
注意:==、!=、<、>、<=和>=运算符的结果均为bool类型。
下面我们来看一个范例:
01 using System;
02 namespace Microsoft.Example
03 {
04 public class TestCompare
05 {
06 public static void Main(string[] args)
07 {
08 int a = 10; //定义一个a变量
09 int b = 2008; //定义一个b变量
10 bool c = (a == b); //进行比较运算,然后把结果赋予c变量
11 Console.WriteLine("c变量的值为" + c); //输出结果
12 }
13 }
14 }
上述代码中,第10行中使用==运算符进行比较,如果a等于b,得到的结果是true,如果a不等于b,则得到的结果是false。
最后的输出结果:
c变量的值为False
11、is运算符
is运算符用于动态检查对象的运行时类型是否与给定类型兼容,其结果也为bool类型。对于“e is T”表达式而言(其中,e为表达式,T为类型),如果e的结果能够隐式转换为T类型,则该表达式的结果为true,否则为false。
下面我们来看一个范例:
1 using System;
2 namespace Microsoft.Example
3 {
4 public class TestIs
5 {
6 static void Main()
7 {
8 int a = 10; //定义一个a变量
9 bool c = a is object; //进行is运算
10 Console.WriteLine("c变量的值为" + c);
11 }
12 }
13 }
上述代码中, 第9行我们使用is运算符,判断a是否是object类型,我们知道,所有的类型都是继承于object类型的,所有is运算出来的结果是true。
最后的输出结果:
c变量的值为true。
12、as运算符
as运算符用于将一个值显式地转换为一个给定的引用类型。如果转换失败,则返回null。
下面我们来看一个范例:
1 using System;
2 namespace Microsoft.Example
3 {
4 public class TestIs
5 {
6 static void Main()
7 {
8 int a = 10; //定义一个a变量
9 object c = a as object; //进行as运算
10 Console.WriteLine("c变量的值为" + c.GetType());
11 }
12 }
13 }
上述代码中,第9行我们进行了as运算符的运算,把a整数转换为object类型。这里我们需要注意到是,这里转换的是类型的引用,实际内容并没有变化,我们通常称这种行为为装箱操作,这个我们在后面会讲到。as运算符从不引发异常。对于“e as T”表达式而言,e必须是表达式,T必须是引用类型,且运算结果总是能够归属于T类型。
最后的输出结果:
c变量的值为System.Int32
13、赋值运算符
=、*=、/=、%=、+=、-=、<<=、>>=、&=、^=和|=运算符被称为赋值运算符,它们能够为变量、属性、事件或索引器元素赋新值。语法如下:
left expression operator right expression
left expression和right expression分别表示左操作数和右操作数,operator表示运算符,如=、*=、/=等。
示例:使用*=运算符计算两个操作数(a和b,它们的值分别为10和2008)的乘积,并赋值给左操作数b。
int a = 10,b =2008;
int b *= a;
分析:b变量的值为20080。
注意:赋值运算符的左操作数必须是属于变量、属性访问、索引器访问或事件访问类别的表达式。
=运算符称为简单赋值运算符,它将右操作数的值赋予左操作数给定的变量、属性或索引器元素。op=运算符称为复合赋值运算符,“x op= y”表达式相当于“x = x op y”表达式。
下面列出了C#中的全部简化赋值运算符。
运算符的简化操作 等 价 于
x++, ++x x = x + 1
x– –,– –x x = x – 1
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
为什么用两个例子来说明++增量和– –减量运算符?把运算符放在表达式的前面称为前置,把运算符放在表达式的后面称为后置。它们的执行方式有所不同。
1.4.2运算符优先级
当表达式包含多个操作符时,操作符的优先级控制着操作符的计算顺序。例如,表达式x+y*z按x+(y*z)计算,因为操作符*的优先级比+高。操作符的优先级由操作符的相关表达式的定义来确定。
下面概述了这些运算符,并将它们按优先级以从高到低的顺序列出:
基本 x.y f(x) a[x] x++ x-- new typeof checked unchecked
一元 + - ! ~ ++x --x (T)x
乘法 * / %
加法 + -
移位 << >>
关系和类型检测 <? >? <=? >=? is? as
相等 == !=
逻辑 XOR ^
逻辑 OR |
条件 AND &&
条件 OR ||
条件 ? :
赋值 = *= /= %= += -= <<=? >>= &= ^= |=
当操作数出现在具有相同优先级的两个运算符之间时,运算符的执行顺序:
除了赋值运算符外,所有的二元运算符都向左顺序关联,意思是从左向右执行运算。例如,x + y + z 按 (x + y) + z 计算。
赋值运算符和条件运算符 (?:) 向右顺序关联,意思是从右向左执行运算。例如,x = y = z 按 x = (y = z) 计算。
优先级和顺序关联性都可以用括号控制。例如,x + y * z 先将 y 乘以 z 然后将结果与 x 相加,而 (x + y) * z 先将 x 与 y 相加,然后再将结果乘以 z。
在复杂的表达式中,应避免利用运算符优先级来生成正确的结果。使用括号指定运算符的执行顺序,可以使代码更整洁,避免出现潜在的冲突。