编译器直接支持的数据类型称为基元类型。基元类型直接映射到Framework类库中存在的类型。int
直接映射到System.Int32
类型,生成完全相同的IL。
可以假设C#编译器自动在所有源代码文件中添加了以下using
指令。
using int = System.Int32;
CLR支持两种类型,引用类型和值类型。
引用类型总是从托管堆上分配,使用引用类型时,需要注意一些性能问题。如果所有类型都是引用类型,应用程序的性能将显著下降。为了提升简单,常用类型的性能,CLR提供了名为“值类型”的轻量级类型。值类型的实例一般在线程栈上分配,虽然也可以作为字段嵌入一个引用类型的对象中。在代表值类型实例的变量中,并不包含一个指向实例的指针,相反,变量中包含了实例本身的字段。因此,值类型的使用缓解了托管堆中的压力,并减少了一个应用程序在其生存期内需要进行的垃圾回收次数。
任何称为“类”的类型都是引用类型,值类型都称为结构或枚举。所有值类型都是从System.ValueType
派生的,所有值类型都是隐式密封(sealed)的。目的是防止将一个值类型用作其他任何引用类型或值类型的基类型。在C#中,用struct声明的类型是值类型,用class声明的类型是引用类型。
值类型对象有两种形式,未装箱形式和已装箱形式。引用类型总是处于已装箱形式。
引用类型的变量包含的是堆上的一个对象的地址,在创建一个引用类型的变量时,它初始化null,表明引用类型的变量当前不指向一个有效的对象。相反,值类型的变量总是包含其基础类型的一个值,而且值类型的所有成员都初始化为0。
将一个值类型的变量赋给另一个值类型的变量时,会执行一次逐字段的复制。将引用类型的变量赋给另一个引用类型的变量时,只复制内存地址。
两个或多个引用类型的变量能引用堆中的同一个对象,所以对一个变量执行的操作可能影响到另一个变量引用的对象。相反,值类型的变量是自成一体的对象,对一个值类型变量执行的操作,不可能影响另一个值类型变量。
未装箱的值类型不在堆上分配,一旦定义了该类型的实例方法不再处于活动状态,为它们分配的内存就会被释放。
值类型是比引用类型更“轻型”的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用。
当值类型的对象传递给期望得到引用类型参数的方法时,会自动触发装箱操作。
public virtual Int32 Add(Object value);
装箱操作在内部发生了以下事情。
(1)在托管堆中分配好内存。
(2)把值类型的字段复制到新分配的堆内存。
(3)返回对象的地址。
C#编译器会自动生成对一个值类型的实例进行装箱所需的IL代码。已装箱的对象会一直存在于堆中,直到被垃圾回收。装箱后,在线程栈上分配的值类型对象与已装箱的对象就没有关系了。
使用已装箱的对象时,会执行拆箱操作。
(1)获取已装箱对象中各字段的地址。
(2)将这些字段包含的值从堆中复制到基于栈的值类型实例中。