结构有两种用例。不透明结构对于可以使用不可变类实现的事物很有用,但足够小,即使在最好的情况下,使用类也不会有太多(如果有的话)好处,特别是如果它们的频率被创建和丢弃是它们被简单复制的频率的很大一部分。例如,Decimal
是一个 16 字节的结构,因此保存一百万个Decimal
值将占用 16 兆字节。如果它是一个类,对Decimal
实例的每个引用将占用 4 或 8 个字节,但每个不同的实例可能会占用另外 20-32 个字节。如果有许多大型数组,其元素是从少数不同的Decimal
实例,类可能会胜出,但在大多数情况下,更可能有一个数组,其中有一百万个引用指向一百万个不同的实例Decimal
,这意味着结构会胜出。
以这种方式使用结构通常只有在 MSDN 引用的指南适用的情况下才是好的(尽管不变性指南主要是由于尚无任何方式可以指示它们修改底层结构的结构方法)。如果最后三个准则中的任何一个不适用,那么使用不可变类可能比使用结构更好。但是,如果第一个准则不适用,则意味着不应该使用不透明的结构,而不是应该使用类。
在某些情况下,数据类型的目的只是将一组变量用胶带固定在一起,以便它们的值可以作为一个单元传递,但它们在语义上仍然作为不同的变量保留。例如,许多方法可能需要传递代表 3d 坐标的三个浮点数组。如果要画一个三角形,传递三个Point3d
参数比传递九个浮点数要方便得多。在许多情况下,此类类型的目的不是赋予任何特定于域的行为,而是简单地提供一种方便地传递事物的方法。在这种情况下,如果使用得当,结构可以提供优于类的主要性能优势。应该表示三个类型变量的结构double
用胶带固定在一起的应该简单地具有三个公共字段类型double
。这样的结构将允许有效地执行两种常见的操作:
- 给定一个实例,对其状态进行快照,以便可以在不干扰快照的情况下修改实例
- 给定一个不再需要的实例,以某种方式想出一个稍微不同的实例
不可变类类型允许第一个以固定成本执行,而不管该类持有的数据量如何,但它们在第二个方面效率低下。变量应该表示的数据量越大,执行第一个操作时不可变类类型相对于结构的优势就越大,执行第二个操作时暴露字段结构的优势就越大。
可变类类型在第二个操作占主导地位的情况下可能很有效,并且很少需要第一个操作,但是对象很难在可变类对象中公开当前值而不将对象本身暴露给外部修改。
请注意,根据使用模式,大型暴露字段结构可能比不透明结构或类类型更有效。大于 17 字节的结构通常比较小的结构效率低,但它们仍然比类更有效。此外,将结构作为ref
参数传递的成本不取决于其大小。如果通过属性而不是字段访问大型结构,通过不必要的值传递它们等,则大型结构效率低下。但如果小心避免冗余的“复制”操作,则存在类与类没有盈亏平衡点的使用模式。结构——结构只会表现得更好。
有些人可能会对类型具有暴露字段的想法感到恐惧,但我建议我所描述的结构不应被视为其自身的实体,而应被视为所读内容的扩展或写下来。例如:
public struct SlopeAndIntercept
{
public double Slope,Intercept;
}
public SlopeAndIntercept FindLeastSquaresFit() ...
将对一堆点执行最小二乘拟合的代码必须做大量工作才能找到结果线的斜率或 Y 截距;找到两者都不会花费更多。调用该FindLeastSquaresFit
方法的代码可能希望在一个变量中具有斜率,而在另一个变量中具有截距。如果这样的代码:
var resultLine = FindLeastSquaresFit();
结果将是有效地创建两个变量resultLine.Slope
,并且resultLine.Intercept
该方法可以根据需要对其进行操作。的领域resultLine
不真正属于SlopeIntercept
,也不属于FindLeastSquaresFit
;它们属于声明的代码resultLine
。如果将该方法用作以下情况,情况几乎没有什么不同:
double Slope, Intercept;
FindLeastSquaresFit(out Slope, out Intercept);
在这种情况下,很明显,在函数调用之后,这两个变量具有由方法分配的含义,但它们在任何其他时间的含义将取决于方法对它们的其他作用。对于上述结构的字段也是如此。
在某些情况下,使用不可变类而不是透明结构返回数据可能会更好。除此之外,使用类将使返回 a 的函数的未来版本更容易返回Foo
包含附加信息的内容。另一方面,在许多情况下,代码会期望处理一组特定的离散事物,而改变这组事物将从根本上改变客户必须处理的事情。例如,如果有一堆处理 (x,y) 点的代码,添加“z”坐标将需要重写该代码,并且“点”类型无法缓解这种情况。