条框7:将值类型尽可能实现为具有常量性和原子性的类型
常量性——自创建后保持不变,无法修改;线程安全。
我们需要注意常量类型中的可变引用类型字段,在实现构造器的时候,需要对其中的可变类型进行防御性的复制。
初始化常量类型通常有三种策略:1.定义构造器;2.创建一个工厂方法;3.创建一个可变的辅助类来解决。常量性——自创建后保持不变,无法修改;线程安全。
我们需要注意常量类型中的可变引用类型字段,在实现构造器的时候,需要对其中的可变类型进行防御性的复制。
Code
条款8:确保0为值类型的有效状态
.Net系统的默认初始化机制会将所有的对象设置为0。因此我们应该将0作为值类型的默认值。在显式赋值创建枚举类型时,一定要保证0为有效。
条款9:理解几个相等判断之间的关系
C#提供了四种不同的函数来判断两个对象是否“相等”:
Code
对于前两个静态函数,我们永远都不应该去重新定义。我们通常需要创建自己的Equals方法,来为自定义的类型定义“相等”语义。第一个方法判断两个不同变量的对象标识是否相等。
第二个方法在内部实现上调用了left对象的第三个和第四个方法来完成验证。
第三个方法存在两种重写方式。其一是引用型重写;另一种是值型重写。前者只有在我们希望更改其预定义的语义(与第一个方法同义)时才应该重写,而在大多数情况下都应该提供我们自己的值类型重写方法。值类型的相等关系中有许多隐含的装箱操作(条款17)。标准的实现模式:
Code
另外重写Equals()方法的同时,也要重写GetHashCode()方法(条款10)。第四个相等判断方法,只要我们创建的是值类型,都需要重定义。
条款10:理解GetHashCode()方法的缺陷
如果我们定义的类型在容器中不会被当作键来使用,那就没什么问题。
一旦我们自己实现重写版本,需要遵循以下三条规则:
1.如果两个对象相等,它们必须产生相同的散列码——主要由类型的第一个字段是否参与相等判断决定;
2.方法的返回值必须是实例不变式,即返回相同的值——由第一个字段的常量性决定;
3.对于所有的输入,散列函数应该在所有整数中产生一个随机的分布——依赖第一个字段的类型与使用情况。