1. 概述
本章讨论MS.NET Framework开发人员经常接触到的各种类型。
2. 名词解释
① 基元类型:编译器直接支持的数据类型。
② 装箱:将一个值类型转换成一个引用类型。
③ 拆箱:获取已装箱的对象中的各个字段的地址。
3. 主要内容
3.1 编程语言的基元类型
作者建议开发人员开发中使用FCL类型名称,不要使用简化的基元类型名称。原因如下:
① 简化名称的映射容易混淆。
② 不同的编程语言,规则不一。
③ 影响代码的阅读。
④ 时间长了容易忘掉多语言环境的支持。
checked 和 unchecked 基元类型操作。
两个特殊的类型:System.Decimal 和 System.Numerics.BigInteger .
3.2 引用类型和值类型
声明值类型的条件:
① 类型具有基元类型的行为。(建议全部字段都标记为readonly)
② 类型不需要从其他任何类型继承,也不会派生出其他任何类型。
③ 类型的实例较小(<=16b),或者不作为方法的实参传递,也不从方法返回。
* 用StructLayoutAttribute来控制类型中的字段布局。
3.3 值类型的装箱和拆箱
装箱的内部逻辑:
① 在托管堆中分配内存。(包括各个字段和两个额外成员-类型对象指针和同步块索引)。
② 值类型的字段复制到新分配的堆内存。
③ 返回对象的地址。
拆箱的内部逻辑:
① 如果变量为null,抛出 NullReferenceException异常。
② 如果类型不符,抛出 InvalidCastException 异常。
③ 返回当前对象各个字段的托管堆地址。
3.3.1 使用接口可以更新已装箱值类型中的字段,但是不建议这么做。
3.3.2 对象 相等性 和 同一性
定义自己的类型时,如果要重写Equals,必须确定符合相等性的四个特征:
① 自反的。 x.Equals(x)肯定返回true。
② 对称的。x.Equals(y)肯定返回与y.Equals(x)相同的值。
③ 可传递的。
④ 一致的。
可能还需要:
① 让类型实现 System.IEquatable<T>接口的Equals方法。
② 重载==和!=操作符方法。
3.4 对象哈希码
选择一种算法来计算类型实例的哈希码时,请遵守以下规则:
① 这个算法要提供良好的随机分布,使哈希表获得最佳性能。
② 可以调用基类的GetHashCode方法并包含它的返回值。不建议调用Object或者ValueType的GetHashCode方法。
③ 这个算法应该使用至少一个实例字段。
④ 这个算法使用的字段应该是不可变的。
⑤ 这个算法应该尽可能快的执行。
⑥ 包含相同值的不同对象应返回相同的哈希码。
3.5 dynamic基元类型
为了方便开发人员使用反射或者与基本组件通信,C#编译器允许将一个表达式的类型标记为dynamic。
代码使用dynamic表达式/变量来调用一个成员时,编译器会生成特殊的IL代码来描述所需的操作。这种代码成为payload。
在运行时,payload代码根据当前由dynamic表达式/变量引用的对象的实际类型来决定具体执行的操作。
4. 总结
熟练掌握装箱拆箱的机制,有助于写出高效的代码。