那么我的问题非常直接 - "拥有值类型的目的是什么?通过复制结构而不是仅创建对它的新引用,我们得到了什么好处?"。
在我看来,只拥有像Java中那样的引用类型会更容易。
编辑:为了澄清这一点,我不是指小于8个字节(引用的最大大小)的值类型,而是指大于或等于8个字节的值类型。
例如-包含四个"int"值的"Rectangle"结构。
一个一字节值类型的实例占用一个字节的空间。引用类型则占用引用本身的空间以及同步块和虚拟函数表等额外空间...
复制引用时,只需复制四(或八)个字节的引用。要复制四字节整数,只需复制四个字节的整数。复制小型值类型的成本不比复制引用高。
不包含引用的值类型根本不需要被垃圾回收器检查。每个引用都必须由垃圾回收器跟踪。
值类型通常比引用类型更具性能优势:
引用类型需要额外的内存进行引用和解引用时的性能开销。
值类型不需要额外的垃圾回收,它会与其所在的实例一起进行垃圾回收。方法中的局部变量在方法结束时被清除。
值类型数组与缓存结合使用非常高效。(例如,将整数数组与 Integer
类型的实例数组相比较)
array, object header: 12
array, length: 4
array, data: 4*(1 million) = 4m
1 million objects, headers = 12 * (1 million)
1 million objects, data padded to 4 bytes: 4 * (1 million)
这就是为什么在大型数组中使用值类型 可能 是一个好主意。
如果您的数据很小(<16字节),您有很多实例和/或经常操作它们,特别是传递给函数,那么收益将可见。这是因为与创建小值类型实例相比,创建对象相对昂贵。正如其他人指出的那样,对象需要被收集,这甚至更加昂贵。此外,非常小的值类型所占用的内存比它们的引用类型等效物要少。
.NET中非原始值类型的示例是Point结构(System.Drawing)。
每个变量都有其生命周期,但并非每个变量都需要灵活性以使您的变量执行高效,但不在堆中进行管理。
值类型(结构体)包含其数据分配在堆栈中或在结构体中内联分配。引用类型(类)存储对值的内存地址的引用,并分配在堆上。
拥有值类型的目的是什么? 值类型非常有效地处理简单数据,(应该用于表示不可变类型来表示值)
值类型对象不能分配在垃圾回收的堆上,表示对象的变量不包含指向对象的指针;变量包含对象本身。
与仅创建对其的新引用相比,复制结构体可以获得什么好处?
如果复制结构体,则C#将创建对象的新副本,并将对象的副本分配给单独的结构体实例。但是,如果复制类,则C#将创建对对象的引用的新副本,并将引用的副本分配给单独的类实例。结构体不能有析构函数,但类可以有析构函数。
Rectangle
,如果有n个类型为Rectangle
的存储位置,那么可以确定有n个不同的Rectangle
实例。如果有一个长度至少为2的类型为Rectangle
的数组MyArray
,像MyArray[0] = MyArray[1]
这样的语句将会把MyArray[1]
的字段复制到MyArray[0]
的字段中,但它们仍然指向不同的Rectangle
实例。如果执行一条语句MyArray[0].X += 4
,那么将修改一个实例的X
字段,而不会修改任何其他数组槽或Rectangle
实例的X
值。需要注意的是,创建数组时会立即填充可写的Rectangle
实例。Rectangle
是可变的类类型。创建一个包含可变Rectangle
实例的数组需要首先给数组一个维度,然后为数组中的每个元素分配一个新的Rectangle
实例。如果想要将一个矩形实例的值复制到另一个矩形实例,就必须执行类似于MyArray[0].CopyValuesFrom(MyArray[1])
的操作(如果MyArray[0]
没有被赋予对一个新实例的引用,这当然会失败)。如果不小心执行了MyArray[0] = MyArray[1]
,那么写入MyArray[0].X
也会影响MyArray[1].X
。这很恶心。
需要注意的是,在C#和vb.net中有一些地方,编译器会隐式复制值类型,然后像原始值一样对副本进行操作。这是一种非常不幸的语言设计,促使一些人提出了价值类型应该是不可变的建议(因为大多数涉及隐式复制的情况只会导致可变值类型的问题)。在编译器非常糟糕的警告语义上可疑的复制将产生错误行为的情况下,这种想法可能是合理的。然而,今天应该被认为已经过时,因为任何体面的现代编译器都会在大多数情况下标记错误,其中隐式复制将产生破碎的语义,包括所有仅通过构造函数、属性设置器或公共可变字段的外部赋值来改变结构的情况。像MyArray[0].X += 5
这样的语句比MyArray[0] = new Rectangle(MyArray[0].X + 5, MyArray[0].Y, MyArray[0].Width, MyArray[0].Height)
更易读。
int
)不是。 - Ben Voigtint
,bool
,double
等),在Java中一切都是引用类型。 - Acidic