C#值类型和结构类型
在向大家详细介绍C#值类型和C#结构类型之前,首先让大家了解下类型设计,然后全面介绍C#值类型和C#结构类型。
条款讨论的是类型设计时候的tradeoff――是将类型设计为结构还是类。Bill Wagner先生给出了一个原则“C#值类型用于存储数据,引用类型用于定义行为(value types store values and reference types define behavior)”。
如何判断这个原则的适用性,Bill Wagner也给出了一个方法,那就是首先回答下面几个问题:
1.该类型的主要职责是否用于数据存储?
2.该类型的公有接口是否都是一些存取属性?
3.是否确信该类型永远不可能有子类?
4.是否确信该类型永远不可能具有多态行为?
如果所有问题的答案都是yes,那么就应该采用C#值类型。这样的判断确实有很好的理由支撑,但是我个人认为“将这4个问题回答为yes”还不足以构成采用C#值类型的全部理由。因为在很多项目实践中,我发现C#值类型带来的性能问题不可小视。C#值类型带来的性能问题主要有两个:
1.由于C#值类型实例在栈和托管堆之间的转换而导致的box/unbox,以及由此带来的托管堆上的垃圾。
2.C#值类型默认情况下采用的是值拷贝语义,如果是比较大的C#值类型,在传递参数和函数返回值时,同样会带来性能问题。
关于第1条,Bill Wagner在本条款中提到了“引用类型会给垃圾收集器带来负担”这个表面看似正确的判断。但是由于box/unbox的效应,有些情况下,反倒是C#值类型给垃圾收集器带来了更多的负担。比如将一些C#值类型放到一个集合中,然后又频繁地对其进行读写操作。如果碰到这种情况,我想“放弃结构而采用类”未尝不是一种更好的做法。事实上,将一个用作数据存储的C#值类型(比如System.Drawing.Point)添加到一个集合(System.Collections.ArrayList)中是一个太常见不过的操作。不过,C# 2.0中新引入的泛型技术对box/unbox的问题有极大的改善。