When should I use a struct instead of a class?
msdn说当需要轻量级对象时应该使用结构。当结构比类更可取时,是否存在其他方案?
有些人可能忘记了:
我理解结构和类之间的技术差异,我只是不知道何时使用结构。
msdn有答案:在类和结构之间进行选择。
基本上,这一页给了你一个4项的清单,并说除非你的类型符合所有的标准,否则就使用一个类。
Do not define a structure unless the
type has all of the following
characteristics:
- It logically represents a single value, similar to primitive types
(integer, double, and so on).- It has an instance size smaller than 16 bytes.
- It is immutable.
- It will not have to be boxed frequently.
我很惊讶我没有读到之前的答案,我认为这是最重要的方面:
当我需要一个没有标识的类型时,我使用结构。例如,三维点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public struct ThreeDimensionalPoint { public readonly int X, Y, Z; public ThreeDimensionalPoint(int x, int y, int z) { this.X = x; this.Y = y; this.Z = z; } public override string ToString() { return"(X=" + this.X +", Y=" + this.Y +", Z=" + this.Z +")"; } public override int GetHashCode() { return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2); } public override bool Equals(object obj) { if (!(obj is ThreeDimensionalPoint)) return false; ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj; return this == other; } public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2) { return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z; } public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2) { return !(p1 == p2); } } |
如果您有这个结构的两个实例,那么您不关心它们是内存中的单个数据块还是两个。你只关心他们所拥有的价值。
比尔·瓦格纳在他的书《有效的C》(http://www.amazon.com/effective specific ways improve your/dp/0321245660)中对此有一章。他的结论是,使用以下原则:
Is the main responsability of the type data storage? Is its public interface defined entirely by properties that access or modify its data members? Are you sure your type will never have subclasses? Are you sure your type will never be treated polymorphically? If you answer 'yes' to all 4 questions: use a struct. Otherwise, use a
class.
当需要值类型语义而不是引用类型时,请使用结构。结构是按值复制的,所以要小心!
另请参见前面的问题,例如
.NET中的struct和class有什么区别?
我会在以下情况下使用结构:
对象应该是只读的(每次传递/分配一个结构时,它都会被复制)。当涉及多线程处理时,只读对象非常好,因为在大多数情况下,它们不需要重新请求锁定。
物体很小,寿命很短。在这种情况下,很有可能将对象分配到堆栈上,这比将其放在托管堆上要高效得多。更重要的是,一旦对象超出其作用域,它所分配的内存就会被释放。换句话说,垃圾收集器的工作更少,内存使用效率更高。
如果使用:
- 它的身份很重要。当按值传递给方法时,结构被隐式复制。
- 它将占用大量的内存。
- 它的字段需要初始值设定项。
- 您需要从基类继承。
- 你需要多态行为;
在以下情况下使用结构:
- 它将像一个基元类型(int、long、byte等)一样工作。
- 它必须有很小的内存占用。
- 您正在调用一个P/Invoke方法,该方法要求通过价值。
- 您需要减少垃圾收集对应用程序性能的影响。
- 其字段只需初始化为其默认值。对于数值类型,该值为零;对于布尔类型,该值为假;对于引用类型,该值为空。
- 注意,在C 6.0中,结构可以有一个默认的构造函数,用于初始化结构的字段转换为非默认值。
- 不需要从基类继承(ValueType除外,从该类继承所有结构都继承)。
- 你不需要多态行为。
当我想将一些值组合在一起以便从方法调用中传递内容时,我总是使用结构,但在读取这些值之后,我不需要将其用于任何内容。就像保持清洁一样。我倾向于把结构中的事物看作是"一次性的",而把类中的事物看作是更有用和"实用的"。
如果一个实体是不可变的,那么使用结构或类的问题通常是性能问题而不是语义问题。在32/64位系统上,无论类中的信息量如何,类引用都需要存储4/8字节;复制类引用将需要复制4/8字节。另一方面,每个不同的类实例除了包含的信息和对它的引用的内存开销外,还将有8/16字节的开销。假设需要一个由500个实体组成的数组,每个实体包含四个32位整数。如果实体是结构类型,则无论500个实体是否都相同、都不同或介于两者之间,数组都将需要8000个字节。如果实体是类类型,500个引用的数组将占用4000个字节。如果这些引用都指向不同的对象,那么每个对象将需要额外的24个字节(所有500个对象需要12000个字节),总共需要16000个字节——是结构类型存储成本的两倍。另一方面,代码创建了一个对象实例,然后复制了对所有500个数组槽的引用,该实例的总开销为24字节,数组的总开销为4000字节,总共4024字节。一大笔节省。很少有情况能像最后一种情况一样有效,但在某些情况下,可能可以将一些引用复制到足够的阵列插槽中,以使这种共享变得有价值。
如果实体应该是可变的,那么是否使用类或结构的问题在某些方面更容易处理。假设"thing"是一个结构或类,它有一个名为x的整型字段,并执行以下代码:
1 2 3 4 | Thing t1,t2; ... t2 = t1; t2.x = 5; |
是否希望后一个语句影响T1.X?
如果事物是一个类类型,T1和T2是等价的,这意味着T1.x和T2.x也是等价的。因此,第二条语句将影响T1.X。如果事物是结构类型,T1和T2将是不同的实例,这意味着T1.X和T2.X将引用不同的整数。因此,第二个语句不会影响T1.X。
可变结构和可变类具有根本不同的行为,尽管.NET在处理结构突变时有一些奇怪之处。如果需要值类型行为(即"t2=t1"将数据从t1复制到t2,同时将t1和t2保留为不同的实例),并且如果可以使用.NET对值类型处理中的怪癖,请使用结构。如果你想要值类型语义,但是.NET的怪癖会导致你的应用程序中的值类型语义被破坏,那么就使用一个类并含糊不清。
除此之外,上述优秀答案:
结构是值类型。
它们永远不会被设置为零。
将结构设置为"无",将其所有值类型设置为其默认值。
正如@simon所说,结构提供了"值类型"语义,因此如果您需要类似于内置数据类型的行为,请使用结构。因为结构是通过copy传递的,所以您需要确保它们的大小很小,大约16个字节。
当你真的不需要行为,但你需要比简单的数组或字典更多的结构。
跟进这就是我对结构的一般看法。我知道他们可以有方法,但我喜欢保持这种整体的精神差异。
结构在堆栈上,而不是堆上,因此它们是线程安全的,在实现传输对象模式时应该使用,您永远不希望使用堆上的对象,它们是易失的,在这种情况下,您希望使用调用堆栈,这是使用结构的基本情况,我对这里所有的答案感到惊讶,
隐马尔可夫模型。。。
我不会将垃圾收集用作支持/反对使用结构与类的参数。托管堆的工作原理与堆栈非常相似——创建对象只需将其放在堆的顶部,这几乎与在堆栈上分配一样快。另外,如果一个对象是短期的,并且在GC循环中不存在,那么释放是免费的,因为GC只使用仍然可以访问的内存。(搜索msdn,有一系列关于.NET内存管理的文章,我懒得去挖掘它们)。
大多数时候,我使用一个结构时,我会因为这样做而自责,因为后来我发现使用引用语义会使事情变得简单一点。
不管怎样,上面发表的msdn文章中的这四点似乎是一个很好的指导方针。
我认为最好的答案就是在需要属性集合时使用struct,在需要属性和行为集合时使用class。