ValueType 是个善意的谎言。
内置的数值类型(int、long、byte)、char、enums和structs都是值类型。
这意味着它们对对象类型具有不同的标识和等效概念。如果我这样做 x = y 并且 x 和 y 是引用类型,那么 x 和 y 现在指向完全相同的对象。但是,如果我这样做 x = y 并且 x 和 y 是值类型,那么 x 和 y 现在是两个完全不同的对象,它们恰好是相同的。 (这也反映在== 和Equals 中,尽管可以覆盖)。
(这是人们谈论堆栈和堆的地方,如果他们还没有,那真的是一个实现细节,虽然重要,但不是值和引用类型之间的区别点)。
现在,基本上这一切都很好,但是关于引用类型的一件事是它们都受益于从 System.Object 继承。值类型 int 并不是真的,这也很好,因为它在许多方面都好得多,它只是由非常擅长这样做的可爱的 CPU 指令处理的四个字节的内存。不过,有时将 int 视为继承自 System.Object 有时很有用,您也可以这样做。
当然,这意味着您可以使用 int 执行仅对 System.Object 有意义的操作,因此当您这样做时,int 被“装箱”,然后可以再次“拆箱”。
这很好,但是如果我们想做一些特定于值类型的事情怎么办?更重要的是,如果 CLR 的设计者这样做了(特别是,他们想要一个 GetHashCode 用于与上述基于值的等价相关的值类型,而不是对象所具有的基于身份的等价)?
为此我们有ValueType。系统将所有值类型视为继承自此类,而后者又继承自 Object。枚举依次继承自值类型,所有枚举类型都继承自它,从而允许所有枚举中的一些通用功能。
因此,如果您想处理所有值类型的超类,请使用 ValueType,但如果您想实际创建值类型,请根据需要创建结构或枚举。
通用类型系统解释:
结构是从 System.ValueType 隐式派生的值类型,而 System.ValueType 又从 System.Object 派生。结构对于表示内存需求较小的值以及将值作为按值参数传递给具有强类型参数的方法非常有用。在 .NET Framework 类库中,所有原始数据类型(Boolean、Byte、Char、DateTime、Decimal、Double、Int16、Int32、Int64、SByte、Single、UInt16、UInt32 和 UInt64)都定义为结构。
与类一样,结构定义了数据(结构的字段)和可以对该数据执行的操作(结构的方法)。这意味着您可以调用结构上的方法,包括在 System.Object 和 System.ValueType 类上定义的虚拟方法,以及在值类型本身上定义的任何方法。换句话说,结构可以具有字段、属性和事件,以及静态和非静态方法。您可以创建结构的实例,将它们作为参数传递,将它们存储为局部变量,或者将它们存储在另一个值类型或引用类型的字段中。结构也可以实现接口。
值类型在几个方面也与类不同。首先,虽然它们隐式继承自 System.ValueType,但它们不能直接继承自任何类型。同样,所有值类型都是密封的,这意味着不能从它们派生其他类型。它们也不需要构造函数。
对于每个值类型,公共语言运行时都提供一个对应的装箱类型,这是一个与值类型具有相同状态和行为的类。值类型的实例在传递给接受 System.Object 类型参数的方法时会被装箱。当控制从接受值类型作为按引用参数的方法调用返回时,它被取消装箱(即,从类的实例转换回值类型的实例)。当需要装箱类型时,某些语言要求您使用特殊语法;其他人在需要时会自动使用盒装类型。定义值类型时,您同时定义了装箱和未装箱类型。
ValueType 的奇怪之处在于允许上述情况发生。