天天看点

《CLR via C#》精髓:引用类型和值类型一、引用类型和值类型的区别二、值类型的特点三、何时才需定义值类型

一、引用类型和值类型的区别

  1. 引用类型分配于托管堆上;值类型分配于线程栈上。由此可见,类型的实例最终分配在栈上还是堆上,由类型的定义者决定而不是由类型的使用者决定(这点与非托管的C/C++不同)。
  2. 引用类型实例存储了对象数据在托管堆上的地址;值类型实例存储了自身的数据。
  3. 引用类型受垃圾回收器控制;值类型不受垃圾回收器控制。
  4. 用class声明的类型是引用类型;用struct声明的类型是值类型。
  5. 引用类型总是处于已装箱的形式;值类型对象有两种可能的形式:未装箱和已装箱。
  6. 引用类型所继承的Equals方法(由System.Object实现)比较两个引用类型实例所持有的内存地址;值类型所继承的Equals方法(由System.ValueType重写)将比对两个值类型实例的全部字段。GetHashCode方法亦是如此。
  7. 创建一个引用类型变量时,变量被初始化为null,表明引用类型的变量当前不指向任何有效对象;值类型的变量总是包含其基础类型的一个值,并且,值类型的所有成员都初始化为0。
  8. 将一个引用类型变量赋给另一个引用类型变量时,只复制内存地址;将一个值类型变量赋给另一个值类型变量时,会执行一次逐字段的复制。
  9. 两个或多个引用类型变量可以引用堆中的同一个对象,所以,对一个变量执行操作可能影响到另一个变量引用的对象;值类型的变量是自成一体的对象,对一个值类型变量执行的操作不可能影响到其他值类型变量。
  10. 引用类型的实例在其内存被回收时将通过Finalize方法接收通知;值类型实例被回收时不会通过Finalize方法接收通知(事实上,C#不允许为值类型定义Finalize方法)。

二、值类型的特点

  1. 基本数据类型(Boolean、Char、Int32、Int64、Single、Double、Decimal等)都是值类型。
  2. 所有值类型都直接派生自System.ValueType类型,System.ValueType类型本身又直接派生自System.Object类型。
  3. 所有枚举类型都直接派生自System.Enum类型,System.Enum类型本身又直接派生自System.ValueType类型,由此可知,枚举类型都是值类型。
  4. 所有值类型都是隐式密封的(sealed),因此,值类型无法被作为基类使用,例如:你无法将Int32作为基类来定义任何新类型。
  5. 值类型中不存在虚方法,值类型的所有方法都不能是抽象的。
  6. 创建值类型时,无论是否使用了new操作符,C#都会保证类型中的所有字段都初始化为零。唯一的区别在于,如果使用了new操作符,C#会“认为”实例已经初始化了。
  7. 访问一个值类型时,不会出现NullReferenceException(著名的“未将对象引用设置到对象的实例”错误),因为值类型的变量不是指针。

三、何时才需定义值类型

  1. 类型具有基元类型的行为。即:是一个十分简单的类型,没有成员会修改类型的任何实例字段。
  2. 类型无需从其他任何类型继承。
  3. 类型不会派生出其他任何类型。
  4. 类型的实例较小(16字节以内),或者,类型的实例较大(超过16字节),但不会作为方法的实参和返回值。

转载于:https://www.cnblogs.com/xuyanzheng/p/clr-via-csharp-reference-types-and-value-types.html