什么时候应该使用结构而不是类?
MSDN说,你需要使用结构,当你需要轻量级的对象。 当一个结构比一个类更可取时,还有其他的场景吗?
编辑:
有人忘记了:
1. 结构可以有方法!
2. 结构没有继承能力。
另一个编辑:
我了解技术上的差异,我只是没有一个良好的感觉什么时候使用一个结构。
MSDN有答案: 选择类和结构之间 。
基本上,该页面给你一个4项目清单,并说,除非你的类型符合所有标准,否则使用一个类。
除非类型具有以下所有特征,否则不要定义结构:
- 它在逻辑上表示一个单一的值,类似于基本类型(整数,双精度等等)。
- 它的实例大小小于16字节。
- 它是不可改变的。
- 它不会经常被装箱。
我很惊讶,我没有读过任何以前的答案,我认为这是最关键的方面:
当我想要一个没有身份的类型时,我使用结构。 例如一个3D点:
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); } }
如果你有这个结构的两个实例,你不关心它们是内存中的单个数据还是两个。 你只关心他们所持有的价值。
Bill Wagner在他的书“effective c#”( http://www.amazon.com/Effective-Specific-Ways-Improve-Your/dp/0321245660 )中有一章。 他通过使用以下原则得出结论:
- 类型数据存储的主要责任是什么?
- 其公共接口是否完全由访问或修改其数据成员的属性定义?
- 你确定你的类型永远不会有子类吗?
- 你确定你的类型永远不会被多态处理吗?
如果你对所有4个问题回答“是”:使用一个结构。 否则,使用一个类。
当你想要值类型的语义而不是引用类型的时候使用一个结构体。 结构是按值复制的,所以要小心!
另见前面的问题,例如
.NET中的struct和class有什么区别?
我会在下列情况下使用结构:
-
一个对象应该是只读的(每次你传递/分配一个结构被复制)。 只读对象在多线程处理方面非常好,因为在大多数情况下,它们不需要锁定。
-
一个物体小而短暂。 在这种情况下,很有可能将对象分配到堆栈上,这比将其放在托管堆上效率更高。 这个对象分配的内存,一旦超出范围就会被释放。 换句话说,垃圾收集器的工作量较少,内存使用效率更高。
当我想将一些值从方法调用中传回来的时候,我总是使用一个结构体,但是在读取这些值之后,我不需要使用它。 就像保持事物清洁一样。 我倾向于将结构中的东西看作“一次性”,而类中的东西更有用,更“功能化”
如果一个实体将是不可变的,那么是否使用结构或类的问题通常是性能而不是语义。 在32/64位系统上,无论类中的信息量如何,类引用都需要4/8个字节来存储; 复制一个类的引用将需要复制4/8字节。 另一方面,除了它所保存的信息以及引用它的内存开销之外,每个不同的类实例都会有8/16字节的开销。 假设有一个500个实体的数组,每个实体拥有4个32位整数。 如果实体是结构类型,则无论所有500个实体是全部相同的,全部不同的还是其间的某个位置,数组都将需要8000个字节。 如果实体是类类型,则500个引用的数组将占用4,000个字节。 如果这些引用都指向不同的对象,那么这些对象将需要每个额外的24个字节(对于所有的500个12000字节),总共16000字节 – 是结构类型的两倍存储成本。 另一方面,在创建一个对象实例的代码中,然后复制一个对所有500个阵列插槽的引用,总成本为该实例的24个字节,阵列的总成本为4000个,总共为4,024个字节。 主要节省。 几乎没有什么情况可以和最后一个一样,但在某些情况下,可能会复制一些引用到足够的数组插槽,以使这种共享值得。
如果实体应该是可变的,那么是否使用类或结构的问题在某种程度上更容易。 假设“Thing”是一个结构体或类,它具有一个称为x的整数字段,其中一个代码如下:
事情t1,t2; ... t2 = t1; t2.x = 5;
是否希望后者的声明影响t1.x?
如果Thing是一个类的类型,t1和t2将是等价的,这意味着t1.x和t2.x也是等价的。 因此,第二个陈述将影响t1.x. 如果Thing是一个结构类型,则t1和t2将是不同的实例,这意味着t1.x和t2.x将引用不同的整数。 因此,第二个陈述不会影响t1.x.
可变结构和可变类具有根本不同的行为,但.net在处理结构变异方面有一些怪癖。 如果需要值类型的行为(意思是“t2 = t1”将数据从t1复制到t2,而将t1和t2作为不同的实例),如果能处理.net处理值类型时的怪癖,一个结构。 如果一个人想要价值型的语义,但是.net的怪癖会导致应用程序中的价值型语义崩溃,那么就使用一个类和嘟</s>。
在以下情况下使用课程
- 其身份是重要的。 按值传递给方法时,结构会被隐式复制。
- 它将有一个大内存足迹。
- 它的字段需要初始化器。
- 你需要从一个基类继承。
- 你需要多态行为;
使用结构如果:
- 它会像一个原始类型(int,long,byte等)。
- 它必须有一个小的内存占用。
- 您正在调用一个P / Invoke方法,该方法需要一个结构按值传递。
- 您需要减少垃圾回收对应用程序性能的影响。
- 其字段只需要初始化为其默认值。 对于数字类型,此值为零,对于布尔类型为false,对于引用类型为null。
- 请注意,在C#6.0中,结构可以有一个默认的构造函数,可以用来将结构的字段初始化为非默认值。
- 您不需要从基类继承(除了所有结构继承的ValueType之外)。
- 你不需要多态的行为。
当你不需要行为的时候,但是你需要比简单的数组或字典更多的结构。
跟进这就是我对一般结构的看法。 我知道他们可以有方法,但我喜欢保持整体的心理区别。
正如@Simon所说,结构提供了“值类型”的语义,所以如果你需要类似于内置数据类型的行为,使用一个结构。 由于结构是通过副本传递的,所以你要确保它们的大小很小,约为16个字节。
另外上面的优秀答案:
结构是值类型。
他们永远不能被设置为Nothing 。
设置一个结构= Nothing,将其所有的值类型设置为它们的默认值。
嗯…
我不会使用垃圾收集作为/反对使用结构与类的参数。 托管堆的工作原理与堆栈非常类似 – 创建一个对象只是将其放在堆的顶部,这几乎与在堆栈上分配速度一样快。 另外,如果一个对象是短暂的,并且不能经历一个GC周期,则释放是免费的,因为GC只能处理仍然可访问的内存。 (搜索MSDN,有一系列关于.NET内存管理的文章,我只是懒得去挖掘它们)。
大多数时候我使用一个结构体,结果我踢了自己这样做,因为后来我发现有了引用语义会使事情变得简单一些。
无论如何,以上MSDN文章中的四点似乎是一个很好的指导方针。
结构堆栈不是堆,因此它们是线程安全的,并且应该在实现传输对象模式时使用,你永远不希望在Heap上使用它们是volatile的对象,在这种情况下你想使用Call Stack,这是一个使用结构的基本情况我很惊讶在这里的答案,
我认为最好的答案是简单地使用结构,当你需要的是一个属性集合,当它是属性和行为的集合。