msdn 建议(在另一个答案中列出)提供了一些指导。为了性能,您应该考虑它们的用法以及结构和类之间的区别在哪里很重要。最重要的是只在类型是实际值类型时才使用结构,即具有值语义。将类用于值类型,或将结构用于应该是引用类型的东西,会很快产生令人困惑的结果,导致意外复制或意外引用。还要记住,结构应该始终是不可变的。
只有在对性能极为敏感的情况下,您才应该忽略任何这些基本规则(例如:框架破坏了 List.Enumerator 结构的规则!)。
结构与类的性能注意事项:
如果您将结构体作为参数传递给方法,则每次都会创建该结构体的副本。这就是文档建议不要使结构大于 16 字节的原因。
double Distance(Vector3D a, Vector3D b) // Copies both 24-byte structs
{
return Math.Sqrt((a.X - b.X *....);
}
在上面的场景中,3 个双精度的 3D 向量将产生 24 个字节,并且会比推荐的 16 个更大,但我仍然认为结构是有意义的,因为它显然是一个值类型,尤其是你有一个Vector2D 包含两个双精度(16 字节),是一个结构!
高效使用结构以及它们的性能真正闪耀的关键是将它们用于缓存位置并避免多次分配。重用上面的 Vector 例子,如果你有这个方法
double AverageDistanceFromOrigin(Vector3D[] points) // Single reference passed
{
double sum = 0.0;
for(...)
sum += Math.Sqrt(points[i].X... + ... + ...) // No copies
return sum/points.Length;
}
您可能会看到有利于结构的良好性能差异。原因是现在您将单个引用(数组)传递给该方法,因此每次调用复制结构没有额外的开销(请注意,该方法不会为数组中的每个条目调用距离方法) .
结构数组也像 [x1, y1, z1, x2, y2, ...] 这样在内存中连续布局,因此 cpu 将加载例如一次16个坐标进入缓存,导致缓存未命中率极低。将其与class Vector3D 实现进行比较:现在将分配一个向量数组,并且数组中的每个条目也是一个必须在堆上分配的引用,然后是垃圾回收。该数组现在是一个引用数组 [ref to v1, ref to v2, ref to v3] 每一个都可能有堆中的任何地址,并且不能彼此相邻。这可能导致比结构案例更多的缓存未命中。
- 仅当您的类型具有值语义时才使用结构(反之亦然)。
- 不要将大型结构单独传递给方法;对结构列表/数组进行操作以避免复制
- 不要将 16 字节限制视为硬限制:您可以使用更大的结构,但请记住避免将它们单独传递给方法。