第七章 运算符和强制类型转换
运算符
移位运算符 << >>
成员对象访问运算符 (用于对象和结构) .
索引运算符(用于数组和索引器)[]
委托连接和删除运算符 + —
对象创建运算符 new
类型信息运算符 sizeof is typeof as
溢出异常控制运算符 checked unchecked
间接寻址运算符 []
名称空间别名限定符 ::
空合并运算符 ??
Checked和Unchecked运算符
byte b = 255;
checked
{
b++;
}
Console.WriteLine(b.ToString());
注意,Unchecked是默认行为。只有在需要把几行未检查的代码放在一个显式地标记为checked的大代码块中,才需要显式地使用Unchecked关键字
使用Checked关键字进行溢出检查
Is 运算符
Is 运算符可以检查对象是否与特定的类型兼容。兼容是指该对象或者类型派生于改类型。
int i = 10;
if(i is 0bject)
(
Console.WriteLine("i is an object");
)
as 运算符
as 运算符用于执行引用类型的显式类型转换
如果类型兼容就会转换成功如果类型不兼容就会返回null
as 运算符用于一步进行类型转换,不需要使用is关键字进行类型测试,然后进行类型转换
Sizeof 运算符
Sizeof 运算符可以确定栈中值类型需要的长度(单位是字节)
Console.WriteLine(sizeof(int));
Typeof 运算符
Typeof运算符返回一个特定类型的System.Type对象在反射技术中动态查找对象的相关信息很有用。
? 可空类型和运算符
使用可空类型给应用程序提供一个独特的值。如果在程序中使用可空类型就必须考虑null值在各种运算符一起使用的时候的影响,通常可空类型与一元二元运算符一起使用
如果其中一个操作数是null或者两个操作数是unll那么结果就是null
int?a=null;
int? b = a + 4; // b = nu11
int? c = a*5: // c = null
(??)空合并运算符
??空合并运算符提供了一种快捷的方式,可以处理可空类型和引用类型时表示null可能得值
这个运算符放在两个操作数之间,第一个操作数必须是可空类型或者引用类型,第二个操作数必须与第一个操作数类型相同,或者可以隐含的转换为第一个操作数类型
空合并运算符的计算如下
如果第一个操作数不是null整个表达式就等于第一个操作数的值
如果第一个操作数是null整个表达式就等于第二个操作数的值
int? a = nul;
int b;
b =a ?? 10; // b has the value 10
a=3;
b= a ?? 10; // b has value 3
如果第二个操作数不能隐含地转换为第一个操作数的类型,就生成一个编译错误
类型的安全性
类型转换
只能从较小的整数类型隐式地转换为较大的整数类型,不能从较大的整数类型隐式地转
换为较小的整数类型
可空数据类型转换为其它可空数据类型和普通数据类型一样考虑不会溢出和精度损失在可接受的范围内即可
非可控类型转换为可空类型同上
可空类型不能隐式转换为非可空类型,此时必须进行显示的转换
这是因为可空类型的值可以是null,但非可空类型不能表示这个值。
所有的显式类型强制转换都可能不安全,在应用程序中应包含这样的代码,处理可能失
败的类型强制转换
如果不能转换字符串(例如,要把字符串hello转换为一个整数parse()方法就会通过抛
出一个异常注册一个错误
装箱和拆箱
装箱和拆箱 把值类型转换为引用类型,并把引用类型转换回值类型
装箱用于描述把一个值类型转换为引用类型。运行库会为堆上的对象创建一个临时的引用类型“箱子”
int myIntNMer = 20;
object myobject =myIntNmer;
该转换可以是隐式的也可以是显示的
拆箱用于描述相反的过程,其中以前装箱的值类型强制转换回值类型。这里使用术语“强制转换”是因为这种转换是显式进行的。其语法类似于前面的显式类型转换
int myIntNmer=20;
object myobject =myIntNumber; // Box the int
int mysecondNumber = (int)myobject; // Unbox it back into an int
只能对以前装箱的变量进行拆箱。当myobject 不是装箱后的int型时,如果执行最后一行代码,就会在运行期间抛出一个异常。
在拆箱时,必须非常小心,确保得到的值变量有足够的空间存储拆箱的值中 '
的所有字节
比较对象的相等性
比较引用类型的相等性
System.Object定义了三个不同的方法,来比较对象的相等性ReferenceEquals()和两个版本的Equals()和比较运算符==
ReferenceEquals()方法
ReferenceEquals()方法是一个静态方法,测试两个引用是否引用类的同一个实例,特别是两个引用是否包含内存中的相同地址。作为静态方法,它不能重写,所以system.Object的实现代码保持不变
如果提供的两个引用引用同一个对象实例,则RcfcrenceEqualsO总 是返回tue;否则就返回false。
但是它认为null等于null:
虚拟的Equals()方法
Equals()虚拟版本的System.Object实现代码也可以比较引用。但因为这个方法是虚拟的,所以可以在自己的类中重写它,从而按值来比较对象。特别是如果希望类的实例用作字典中的键,就需要重写这个方法,以比较相关值。否则,根据重写Object.GetHashCode()的方式,包含对象的字典类
要么不工作,要么工作的效率非常低。在重写EqualsO方法时要注意,重写的代码不会抛出异常。同理,这是因为如果抛出异常,字典类就会出问题,一些在内部调用这个方法的.NET基类也可能出问题。
静态Equals()方法
Equals()的静态版本与其虚拟实例版本的作用相同,其区别是静态版本带有两个参数,并对它们进行相等性比较。这个方法可以处理两个对象中有一个是null的情况,因此,如果一个对象可能是null,这个方法就可以抛出异常,提供额外的保护。静态重载版本首先检查它传递的引用是否为null。 如果它们都是null,就返回ture(因为null与null相等)。 如果只有一个引用是null,它就返回false。 如果两个引用实际上引用了某个对象,它就调用Equals()的虚拟实例版本。这表示在重写Equa1sO的实例版本时,其效果相当于也重写了静态版本
比较运算符(==)
值类型比较值是否相等应用类型比较是不是同一引用
最好将比较运算符看作严格的值比较和严格的引用比较之间的中间选项
但它的一个明显例子是system.string
类,MicroSoft重写了这个运算符,以比较字符串的内容,而不比较它们的引用
比较值类型的相等性
ReferenceEquals()用于比较引用,Equals()用于比较值,比较运算符可以看作一个中间项。但最大的区别是值类型需要装箱,才能把它们转换为引用,进而才能对它们执行方法
ReferenceEquals()在应用于值类型时,它总是返田false
尽管system,ValueType提供的Equals()的默认重写版本肯定足以应付绝大多数自定义的结构,
但仍可以针对自己的结构再次重写它,以提高性能。另外,如果值类型包含作为字段的引用类型,就需要重写Equals(),以便为这些字段提供合适的语义,因为Equals()的默认重写版本仅比较它们的地址