Astruct
可能会或可能不会在堆栈上分配。引用类型永远不能在堆栈上分配;它们总是在堆上分配。
来自标准 (ISO 23270),第 8.8 节:
8.8 结构
类和结构之间的相似之处很长——结构可以实现接口,并且可以具有与类相同类型的成员。然而,结构与类在几个重要方面有所不同:结构是值类型而不是引用类型,结构不支持继承。结构值存储在“堆栈上”或“内联”。细心的程序员有时可以通过明智地使用结构来提高性能。
例如,对 Point 使用结构而不是类可以在运行时执行的内存分配数量上产生很大差异。下面的程序创建并初始化一个包含 100 个点的数组。
作为Point
一个类实现,101 个单独的对象被实例化——一个用于数组,一个用于 100 个元素。
class Point
{
public int x, y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
class Test
{
static void Main()
{
Point[] points = new Point[100];
for (int i = 0; i < 100; i++)
{
points[i] = new Point(i, i*i);
}
}
IfPoint
被实现为结构,如
struct Point
{
public int x, y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
只有一个对象被实例化——用于数组的那个。Point 实例在数组中内联分配。这种优化可能会被滥用。使用结构而不是类也会使应用程序运行速度变慢或占用更多内存,因为按值传递结构实例会导致创建该结构的副本。
所以答案是“也许”。
对于您的示例,将数组(引用类型)包装在struct
(值类型)中并不意味着什么:该数组仍分配在堆上。
但是,如果您将类更改EdgeData
为结构,则可以(但可能不会)在数组中内联分配它。EdgeData
因此,例如,如果您的类的大小为 16 个字节,并且您创建并填充了EdgeData[]
100 个条目,那么您实际上分配了 1 个数组实例(其后备存储大小可容纳 100 个对象引用和您的EdgeData
类的 100 个单独实例.
如果EdgeData
是一个结构,则分配 1 个数组,其后备存储大小可容纳 100 个EdgeData
实例(在本例中为 1600 个字节,因为我们假设的EdgeData
结构大小为 16 个字节。)
遍历数组的类版本,特别是如果数组非常大,可能会导致分页,因为当您跳过整个堆以访问各个EdgeData
实例时,您可能会丢失引用的局部性。
对数组版本的迭代struct
保留了引用的局部性,因为EdgeData
实例是内联的。