C#值类型与引用类型

C#程序员经常搞不清楚值类型和引用类型的概念和区分,我想说一下。

1. 为什么要有值类型——使用引用类型的代价
引用类型是CLR从托管堆上分配空间而来的,new操作会返回对象的内存地址。在托管堆上分配对象不是没有代价的,包含分配对象时的额外成员(类型对象指针和同步块索引)的初始化操作、对象内部各个字节置零初始化,而且,这个分配操作还可能强制执行一次垃圾收集操作。
当所有类型都是引用类型时,带给托管堆的压力可想而之。并且垃圾收集操作相当耗费时间,这样的开销显然太大。因此需要引入值类型。

2. 值类型的特殊性
从OO上看,在C#中,一切皆是Object,值类型也不例外。但是,值类型在System.Object之上的类型是System.ValueType或者System.Enum(仅枚举类),且,值类型都是sealed类型。
从名称上看,引用类型都是称为类,值类型称为结构或者枚举,SDK中尤其明显。
值类型的实例在线程的堆栈上分配,虽然可以作为字段嵌入一个引用类型中,只不过引用类型的字段存储一个指向托管堆上对象实例的指针,而值类型的字段存储的是该值类型本身。修改的时候在线程堆栈上直接修改。
值类型复制之后是复制内容,修改一个不会影响另一个副本;引用类型复制是复制指针,指向的对象是同一个,因此修改一个会影响另一个副本。

3. 定义自己的值类型
使用class定义一个引用类型,使用struct定义一个值类型。值类型也可以实现一个或者多个接口。
什么时候需要把类型定义成值类型呢?有以下几点:

  • 类型是个基元类型,没有提供可供修改其中字段的方式,即不可变的类型。
  • 不需要继承其他类型。
  • 不会派生出其他类型。

满足以上三点,则满足定义成值类型的充分性,可以考虑定义成值类型。
必要性仍然要考虑类型的大小,因为值类型的复制是传值,而不是简单拷贝指针,大的值类型传递代价比引用类型传递更大。

2 comments

  1. 本来这篇东西我是不会留言的,但是鉴于近日我每篇都是第一个留言的优良习惯以及同济网实在无处可以灌水的局面,我就在这里放肆下了。

    我的批注就是:真是精彩啊~! 可惜我看不懂。

发表评论