假设我有一些小数据类型 - 例如一个简单的点实现。我可以将此类型实现为引用类型(类),也可以实现为值类型(结构)。
public class PointClass
{
public readonly int X;
public readonly int Y;
}
public readonly struct PointStruct
{
public readonly int X;
public readonly int Y;
}
// Not shown, constructors, equality/hashcode members, etc
现在,假设我要创建这些类型的集合。我可能会使用基本数组,或更可能使用标准
System.Collections.Generic.List
,它本身使用支持数组。
对于引用类型,创建
new
实例会将它们分配在堆上,因此这些数据需要在某个时候进行垃圾收集。该数组将包含指向点实例的引用数据,无论它们位于堆上。
或者,如果我使用值类型实现,数组是否包含“原始字节”来容纳
int
字段?也就是说,一个 new
实例将在堆栈上分配,其数据按值复制到数组中,因此只需要对数组本身进行垃圾收集。
我的想法是,第二种方法(使用值类型)更适合存储大量这些小数据类型。当然,我知道“大型”结构所涉及的开销 - 这个示例专门针对小型类型。
这个关于值类型的说法准确吗?或者值类型仍然会有垃圾收集吗?我的理解正确吗,还是我严重忽略了关于值类型的要点?
对于引用类型,创建新实例会在堆上分配它们,因此这些数据需要在某个时候进行垃圾收集。
是的,但代价是存储指针和数据。只有当对象被实际收集时才会有好处。如果它们从未被收集过,那么就没有任何收获。
另请注意,列表会调整大小以适应,但永远不要缩小,除非您告诉他们这样做。因此,在收集整个列表之前,引用所占用的空间通常会丢失。 (显然,对于值类型也是如此,我只是指出增益并不像你想象的那么多。)
如果我要使用值类型实现,数组是否包含“原始字节”来容纳 int 字段?也就是说,一个新实例将在堆栈上分配,其数据按值复制到数组中,因此只需对数组本身进行垃圾收集。
是的,这正是值类型。
...第二种方法(使用值类型)更适合存储大量这些小数据类型。当然,我知道“大型”结构所涉及的开销 - 这个示例专门针对小型类型。
是的,但是现在每次您想要移动这些数据时都会遇到复制问题。一旦您的数据大于指针大小的两倍(在许多情况下这很可能是由于填充造成的),您总是复制的不仅仅是一个指针。 “大”结构不必非常大才会导致问题。
说实话,你不应该真正考虑这些,除非你正在使用真正大量的这些。否则,请编写最有意义的代码,而不必担心性能影响,这在大多数情况下可以忽略不计。