C#值类型与引用类型
C#程序员经常搞不清楚值类型和引用类型的概念和区分,我想说一下。
1. 为什么要有值类型——使用引用类型的代价 引用类型是CLR从托管堆上分配空间而来的,new操作会返回对象的内存地址。在托管堆上分配对象不是没有代价的,包含分配对象时的额外成员(类型对象指针和同步块索引)的初始化操作、对象内部各个字节置零初始化,而且,这个分配操作还可能强制执行一次垃圾收集操作。 当所有类型都是引用类型时,带给托管堆的压力可想而之。并且垃圾收集操作相当耗费时间,这样的开销显然太大。因此需要引入值类型。
2. 值类型的特殊性 从OO上看,在C#中,一切皆是Object,值类型也不例外。但是,值类型在System.Object之上的类型是System.ValueType或者System.Enum(仅枚举类),且,值类型都是sealed类型。 从名称上看,引用类型都是称为类,值类型称为结构或者枚举,SDK中尤其明显。 值类型的实例在线程的堆栈上分配,虽然可以作为字段嵌入一个引用类型中,只不过引用类型的字段存储一个指向托管堆上对象实例的指针,而值类型的字段存储的是该值类型本身。修改的时候在线程堆栈上直接修改。 值类型复制之后是复制内容,修改一个不会影响另一个副本;引用类型复制是复制指针,指向的对象是同一个,因此修改一个会影响另一个副本。
3. 定义自己的值类型 使用class定义一个引用类型,使用struct定义一个值类型。值类型也可以实现一个或者多个接口。 什么时候需要把类型定义成值类型呢?有以下几点:
- 类型是个基元类型,没有提供可供修改其中字段的方式,即不可变的类型。
- 不需要继承其他类型。
- 不会派生出其他类型。
满足以上三点,则满足定义成值类型的充分性,可以考虑定义成值类型。 必要性仍然要考虑类型的大小,因为值类型的复制是传值,而不是简单拷贝指针,大的值类型传递代价比引用类型传递更大。