1. 常用的基本运算符和类型强制转换运算符
1.1 条件运算符(?:)
?:也称三元运算符
语法:条件 ? 条件为真(取值): 条件为假(取值)
int x = 1;
//x等于1时,条件为真,取值为man
//x不等于1时,条件为假,取值为men
var aaa = x == 1 ? "man" : "men";
1.2 checked和unchecked运算符
处理溢出,只要有未预料到的溢出风险,就可以使用checked和unchecked来解决
1. 如果把一个代码块标记为checked,就会进行溢出检查,如果发生了溢出,就会抛出OverflowException异常
//byte只能包含0-255
byte b = 255;
checked
{
//b此时为256,溢出
//报错:System.OverflowException: '算术运算导致溢出。'
b++;
}
Console.WriteLine(b);
2. 如果要禁止溢出检查,则可以把代码标记为unchecked
//byte只能包含0-255
byte b = 255;
unchecked
{
//b此时为256,溢出,溢出的位会被丢弃,所以这里b为0
//不会报错,但会丢失数据,因为byte数据类型不能包含256
b++;
}
Console.WriteLine(b);
3. unchecked是默认行为,只有把代码放在一个显示标记的checked的大代码块中,需要禁止溢出检查,就可以显示的使用unchecked
1.3 is运算符
is检查对象是否与特定的类型兼容。“兼容”表示对象或者是该类型,或者派生自该类型。
int i = 10;
//检查变量i是否与object兼容
if(i is object)
{
//所有类型全部从object继承而来,所以i is object为true,输出下面的句子
Console.WriteLine(i);
}
1.4 as运算符
as运算符用于执行引用类型的显示类型转换,如果要转换的类型与指定的类型兼容,转换就会成功进行,如果类型不兼容,as就会返回null
1. as运算符允许在一步中进行安全的类型转换
object o1 = "Some String";
object o2 = 5;
//s1 = "Some String"
string s1 = o1 as string;
//s2 = null
string s2 = o2 as string;
1.5 sizeof运算符
使用sizeof运算符可以确定栈中值类型需要的长度(单位是字节)
1. 如果对复杂类型(非基本类型)使用sizeof运算符,需要把代码放在unsafe中
//基本类型直接用sizeof来获取字节长度,这里是4,int是32位
Console.WriteLine(sizeof(int));
unsafe
{
Console.WriteLine(sizeof(Racer));
}
1.6 typeof运算符
typeof返回一个表示特定类型的System.Type对象
1. 在使用反射技术动态的查找对象的相关信息的时候,这个运算符很有用
var aa = typeof(Racer);
Console.WriteLine(aa);
1.7 nameof运算符
接受一个符号,属性或方法,并返回其名称
Racer o = null;
//检查o这个变量是否为null
if (o == null)
{
//o是null,则输出参数名称o
throw new ArgumentNullException(nameof(o));
}
1.8 Index运算符
索引运算符可以访问数组
var dic = new Dictionary<string, int>();
dic["aaa"] = 1;
int x = dic["aaa"];
1.9 可空类型和运算符(int?)
可空的值类型
int? a = null;
long? aa = null;
DateTime? dd = null;
int? b = a + 4;//b为null
//比较可空类型时,只要有一个为null,比较结果就是false
if(a >= b)//false
1.10 空合并运算符(??)
??在处理可空类型和引用类型时表示null值的可能性,谁不为null,就取谁的值
1. 第一个操作符必须是一个可空类型或引用类型,第二个操作符必须与第一个操作符的类型相同,或者可以隐式转换为第一个操作数的类型
2. 如果第一个操作数不是null,整个表达式就等于第一个操作数的值
3. 如果第一个操作数是null,整个表达式就等于第二个操作数的值
int? a = null;
int b;
b = a ?? 10;// b的值为10
a = 3;
b = a ?? 10; //b的值为3
//谁不为null,就取谁的值
//如果condition1为null,则这个表达式值为condition2
//如果ondition1不为null,则这个表达式值为condition1
condition1 ?? condition2
1.11 空值传播运算符(?.)
检查空值的条件,null条件操作符在调用方法或属性之前检查操作数是否为null,如果为null,则不执行
1. 使用空值传播运算符(?.)当遇到某个变量为null时,不会抛出值为Null的异常
public class Racer
{
public string name;
public int age;
public char sex;
public Address adre;
public static void ShowData(Racer r)
{
//r?.name 当r为空时,就只返回null,而不继续执行表达式右侧的值,不会把name赋值给fname,fname为null
string fname = r?.name;
int age = r?.age ?? 0;
//对r和Address都进行了null检查,相当于 if(r != null && r.adre != null)
string addr = r?.adre?.city;
}
}
public class Address
{
public string city;
}
2. 运算符的优先级和关联
3. 类型的安全性
3.1 类型转换(隐式和显示)
3.1.1 隐式转换(小到大)
只要能保证值不会发生任何变化,类型转换就可以自动隐式进行
1. 只能从较小的数据类型隐式的转换为较大的数据类型
//小byte到大Long,隐式转换
byte value = 10;
long total;
total = value;
3.1.2 显示转换(大到小)
强制转换用()
1. 使用checked检查类型强制转换是否安全,如果不安全,会抛出一个溢出异常
long val = 300;
//大类型Long转为小类型Int,强制类型转换
int i = (int)val;
//如果不安全,会抛出一个溢出异常
int a = checked((int)val);
3.2 装箱和拆箱
装箱:值类型到引用类型
拆箱:引用类型到值类型
1. 只能对以前装箱的变量进行拆箱
2. 拆箱必须非常小心,确保得到的值变量有足够的空间存储拆箱的值中的所有子节
int my = 20;
//装箱
object obj = my;
//拆箱
int my2 = (int)obj;
4. 比较对象的相等性
4.1 比较引用类型的相等性
4.1.1 ReferenceEquals()
静态方法,测试两个引用是否指向类的同一个实例,特别是两个引用是否包含内存中的相同地址
1. 是静态方法,不能重写
2. 如果两个引用指向类的同一个实例,返回true,否则返回false
Racer x, y;
x = new Racer();
y = new Racer();
bool b1 = ReferenceEquals(null, null);//true
bool b2 = ReferenceEquals(x, y);//false
4.1.2 Equals()虚方法
因为是虚方法,所以可以在自己的类中重写他,可以比较引用,重写后,也可以按值来比较对象
4.1.3 静态的Equals()
与虚版本作用相同,但是静态这个带两个参数,并对它们进行相等性比较
1. 先检查传递给它的引用是否为null,两个都是null,返回true,一个为null,返回false
4.1.4 比较运算符(==)
最好将比较运算符看作严格的值比较和严格的引用比较之间的中间选项。大多数情况下,在比较引用
Racer x, y;
x = new Racer();
y = new Racer();
//引用比较
bool b = (x == y);
4.2 比较值类型的相等性
采用与引用相同的规则:ReferenceEquals()用于比较引用(但是值类型要装箱后才变为引用),Equal()用于比较值,比较运算符==看作一个中间项
5.运算符重载
1. 要求成对重载比较运算符,重载了"==",也必须重载“!=”,否则会产生编译错误
2. 比较运算符必须返回布尔类型的值
6. 关于运算符的其他概念
1. 实现自定义的索引运算符
2. 实现用户定义的类型强制转换
3. 多重类型强制转换