1 操作符概览
操作符(Operation)也译为“运算符”
操作符是用来操作数据的,被操作符操作的数据称为“操作数”
上图的操作符优先级从上至下逐级递减。务必注意数据提升的情况。
2 操作符本质
2.1操作符的本质是函数(即算法)的“简记法”
2.2操作符不能脱离与它关联的数据类型
- 可以说操作符就是与固定数据类型相关联的一套算法的简记法
整数类型的除号进行的是整除。double数据类型的除号进行的是浮点数运算。
- 示例:为自定义数据类型创建操作符
3 操作符优先级
3.1 操作符的优先级
- 可以使用圆括号提高被括起来表达式的优先级;
- 圆括号可以嵌套。
- 不像数学里有方括号,在C#语言中“[]“与”{}”有专门的用途
3.2 同优先级操作符的运算顺序
- 除了带有赋值功能的操作符,同优先级操作符都是自左向右进行计算
- 带有赋值功能的操作符的运算顺序是自右向左
- 与数学运算不同,计算机语言的同优先级运算没有“结合率“
示例:3+4+5只能理解为Add(Add(3,4),5),不能理解为Add(3,Add(4,5))
4 基本操作符
4.1 X++和X--(后置++和后置--)
X++(后置++)当遇到了赋值的时候,先赋值给到等号左边的变量,再执行+1操作;示例如下:
4.2 default
4.3 var操作符
var 声明隐式类型变量,声明后编译器会自动选择对应的变量类型。示例如下:
运行后结果如下:
4.4 new操作符
new 作为操作符主要帮助我们在内存中创建一个类型的实例,并立刻调用这个实例的实例构造器。new操作器主要功能如下
运行结果如下:
new操作符除了可以调用实例的构造器以外,还可以调用实例的初始化器,示例如下:
结果如下:
new操作符为匿名类型创建对象
运行结果如下:
最后一个类型名字即为匿名类型。
其实这是一种比较“偷懒”的方法,而且比较鼓励使用var来声明变量。
new作为关键字有多种用途;
首先看一下继承:
输出结果
使用new作为关键字,示例如下:
运行结果如下:
4.5 checked 和unchecked操作符
运行结果如下
使用check操作符,程序示意如下:
运行结果如下:
我们在程序抛出异常时候,我们一般使用catch语句把异常给抓住,告诉用户说这里有异常产生了。运行程序如下:
运行结果如下:
即打印出了异常情况;
如果使用unchecked语句的话,示例程序如下:
程序运行结果如下:
综上,计算机程序默认的都为unchecked模式。
Checked还有一种使用方式
运行结果如下
4.6 delegate操作符
delegate一般很少作为操作符来使用了,现在已经是一种比较过时的方法了。
首先看一个示例:
程序运行结果为:
窗口点击“Click Me”按钮,出现“Hello World!”的文本。
这里如果不想要这块逻辑被reuse到或者确定这块逻辑不会reuse到,也懒得让它成为一个方法,那么我们可以使用一个匿名方法的语法,具体程序如下:
程序运行结果如下:
与前述结果一致。
这种使用delegate操作符声明匿名方法这种方法是一种过时的方法,现在流行的是使用Lambda表达式(=>)进行声明匿名方法,具体示例程序如下:
得出来的程序运行结果与前述一致。
4.7 sizeof操作符
sizeof操作符是获取一个对象在所在内存中所占字节数这样的一个尺寸。使用sizeof操作符有两点需要注意的地方:
1、默认情况下,sizeof操作符只能用于去获取基本数据类型他们的实例在内存中所占的字节数,基本数据类型即是除了string和object之外的c#关键字里面那些数据类型。原因是sizeof只能获取结构体类型的实例在内存中的字节数,string和object不是结构体数据类型。
2、在非默认情况下,我们可以使用sizeof去获取自定义的结构体类型实例在内存中所占的字节数,但是需要把它放在不安全的上下文中
sizeof操作符使用示例程序如下:
程序出现错误,按照sizeof使用时第二条注意事项,使用sizeof对接自定义的结构体类型需要在不安全的上下文中使用,故需要勾选下图所示的允许不安全代码。勾选后保存即可。
运行程序,得到结果如下:
其中注意一下:在Student结构体中的int类型占了4个字符,long字节占了8个字符。为什么结构体是16而不是4+8=12呢,这个就牵扯到了内存对齐的知识了,属于高级话题,暂时不讨论。
4.8 –>操作符
因为->需要直接操作内存的,所以需要放在不安全的上下文中使用。
为什么使用结构体:在C#中有严格的规定,像指针操作,取地址操作以及用指针去访问成员的操作,只能用来操作结构体类型,不能用他们操作引用类型。
运行程序得到的结果如下:
通过->操作符将数值改为了100
5 一元操作符
一元操作符一般只要有一个操作数跟在操作符后面即可成为表达式。
5.1 &X和*X
&指取地址操作符。*指引用操作符。两者都需要在不安全的上下文中使用,示例程序如下:
程序运行结果如下:
这几种方法都是用在不安全上下文中,而且在实际使用过程中也是比较少见的,大家可以当作知识进行记忆即可,现实工作中很少用到这几个操作符。
5.2 +、-、!、~操作符
几种示例情况如下
运行结果如下:
注意操作符可能会造成数据溢出的情况
运行后结果如下:
对于位数取反操作符~,示例程序如下:
程序运行结果如下:
取非操作符“!”只能操作布尔类型值
程序运行结果如下:
5.3 ++X和--X(前置++和前置--)
--X(前置--)当遇到了赋值的时候,先执行-1操作,再赋值给到等号左边的变量,示例如下:
注意前置++(前置--)与后置++(后置--)在有赋值符号时候的区别。
建议在使用前置或后置++(--)操作符时,尽量独立使用,避免出现异常情况。
5.4 (T)X强制类型转换操作符
5.4.1隐式(implicit)类型转换
- 不丢失精度的转换
- 子类向父类的转换
所有面向对象语言均支持子类向父类的隐式类型转换。
- 装箱
5.4.2 显式(explicit)类型转换
- 有可能丢失精度(甚至发生错误)的转换,即cast
程序运行结果为,在进行显示转换后数据即出现异常:
- 拆箱
- 使用Convert类
程序运行结果如下:
- ToString方法与各数据类型的Parse/TryParse方法
程序运行结果如下:
其中1e2为科学计数法,表示1*10^2=100.但是如果设置成ab,在运行时该程序即会报错。
TryParse通过输出变量(out)来传值。
5.4.3 自定义类型转换操作符
程序运行结果如下:
6 算数运算符
参考《C#定义文档》算术运算符;
7 位移操作符
数据在内存中的二进制的结构,向左或向右进行一定位数的平移。
- 当没有溢出时,左移就是数值乘2,右移就是数据除2。
- 左移不论是正数还是负数,补进来的都是0.而右移的时候,如果操作的是一个正数的话,则补进来的是一个0,如果操作的是一个负数的话,则补进来的数是1.
8 关系操作符
- 关系操作符有两类,第一类是谁大谁小的关系,第二类是判断是否相等的关系。比大小操作符运算级更高。
- 所有关系操作符运算结果一定都是布尔类型的。
9 类型检测操作符
9.1 is操作符
is 操作符执行后的结果也是布尔类型值。
9.2 as操作符
10 逻辑操作符
逻辑与(&)、逻辑或(|)、逻辑异或(^)
一般在操作二进制数据,图像数据时候使用
11 条件操作符
条件与(&&)、条件或(||)。
它们是用来操作布尔类型值,得出的结果也是布尔类型。
注意条件与和条件或具有短路效应。
短路效应示例程序如下:
程序运行结果如下:
如果左边的值为真,则会执行右边的表达式,示例程序如下:
程序执行结果如下:
条件或和条件与的情况类似。实际写代码时候要注意避开这种短路效应
12 null合并操作符
13 条件操作符
三种情况输出结果一致,程序输出结果如下:
14 赋值操作符和lambda表达式
赋值操作符在运算过程中从右向左进行。
Lambda表达式可参照前面的delegate操作符中的内容。