不可变类VS结构(Immutable class vs struct)

2019-08-05 11:46发布

以下是唯一途径类在C#结构(请纠正我,如果我错了)的不同:

  • 类变量的引用,而结构变量的值,因此结构的整个值被复制在分配和参数传送
  • 类变量是存储栈上指向上堆存储器指针,而结构体变量是存储堆作为值

假设我有一个不变的结构,即结构与不能被修改初始化一次场。 每次我通过这个结构作为一个参数或使用中分配,该值将被复制和存储在堆栈中。

然后,假设我有这样的不可变的结构是一个不可变类。 这个类的一个实例将被创建一次,并且仅参照类将在分配和参数通行证被拷贝。

如果对象是可变的,在这两种情况下的行为是不同的:当一个会改变的对象,在第一种情况下的结构的副本将被修改,而在第二种情况下,原来的对象将被改变。 然而,在两种情况下,目的是不可改变的,因此不存在差异,这是否实际上是一类或该对象的用户一个结构。

由于复制引用比复制结构便宜,一个为什么要使用一个不变的结构?

此外,由于可变的结构是邪恶的 ,它看起来像没有理由在所有使用结构。

我在哪里错了?

Answer 1:

由于复制引用比复制结构便宜,一个为什么要使用一个不变的结构?

这并非总是如此。 复制参考将是在64位操作系统,这是潜在地比许多结构更大的8个字节。

还要注意的是类的创作可能更昂贵。 创建一个结构往往是在栈上完全做到了(虽然也有很多例外 ),这是非常快的。 创建类需要创建该对象句柄(垃圾收集),创建的堆栈上的参考,和跟踪对象的生存期。 这会增加GC的压力,这也有一个真正的成本。

话虽这么说,创造了大量不可变结构很可能不是一个好主意,这就是为什么部分指引类和结构之间进行选择的建议总是使用类,如果你的结构将有超过16个字节,它是否会被装箱,并且,使差小等问题。

话虽这么说,我常常依据我的决定对有问题的类型的使用目的和意义。 值类型应该被用来指一个值(再次,参考指引),而且往往比类不同的语意和预期使用。 这使得类或结构之间的选择时,往往只是作为性能一样重要。



Answer 2:

里德的答案是相当不错的,但只是添加一些额外的要点:

请纠正我,如果我错了

你基本上是在这里在正确的轨道上。 你已经取得了令人困惑的变量的共同误差 。 变量是存储位置; 值存储在变量。 而你与常用态神话调情说:“值类型进入堆叠”; 相反, 变量去任何短期长期存储,因为变量的存储位置 。 是否可变的推移短期或长期存储取决于其已知寿命 ,而不是它的类型

但所有这一切是不是你的问题,其归结为寻求这个三段论的驳斥尤其重要:

  • 可变的结构是邪恶的。
  • 参考复制比结构复制便宜,所以不可变的结构总是差。
  • 因此,从不为任何结构使用。

我们可以在几个方面反驳三段论。

首先,是的,可变的结构是邪恶的。 然而,他们有时非常有用,因为在某些受限场景,就可以得到性能上的优势。 除非有其他合理的途径已经用尽我不推荐这种做法并没有一个真正的性能问题。

其次,参考复制不一定比结构复制便宜。 引用通常被实现为4或8个字节管理指针(尽管这是一个实现细节;它们可以被实现为不透明的手柄)。 复制参考大小的结构既不是便宜也不比复制的基准尺寸的参考更昂贵。

第三,即使引用复制比结构复制便宜,引用必须为了解除引用得到他们的领域。 间接引用是不是零成本! 它不仅需要机器周期取消引用的参考, 这样做可能会搞乱了处理器高速缓存 ,并可以使未来的指针引用贵得多!

第四,即使引用复制比结构复制便宜,谁在乎呢? 如果这不是所产生不可接受的性能成本的瓶颈然后哪一个是更快的是完全不相关的。

五,引用是很远很远的内存空间比结构更贵。

六,引用增加费用,因为引用的网络必须由垃圾收集器进行定期跟踪; “blittable”结构可通过垃圾收集器完全被忽略。 垃圾收集是很大一笔开支。

第七,不可变的值类型不能为null,不像引用类型。 你知道每一个值是一个很好的价值。 正如里德指出,为了得到你要同时运行分配器和构造引用类型的一个很好的价值。 这是不便宜。

第八,值类型代表的数值和程序往往约值的操作。 这是有道理的语言来“烤在”两个“价值”和“参考”的隐喻,无论哪个是“便宜”。



Answer 3:

从MSDN ;

类是引用类型和结构是值类型。 引用类型在堆上分配,并且存储器管理是由垃圾收集处理。 值类型分配堆栈或内联的,当他们走出去的范围将被释放。 一般地,值类型是便宜分配和释放。 但是,如果他们在需要装箱和拆箱的显著量的情况下使用,他们表现不佳相比,引用类型。

不要定义一个结构,除非类型有以下所有特点:

  • 它在逻辑上表示单个值,类似于原始类型(整数,双,和等等)。

  • 它有一个实例大小大于16个字节小。

  • 这是不可改变的。

  • 它不会有频繁装箱。

所以,你应该总是使用类而不是结构,如果你的结构将有超过16个字节。 此外,从读http://www.dotnetperls.com/struct



Answer 4:

有两种使用情况的结构。 不透明的结构是东西可以用不变类实现有用的,但足够小,即使在最好的情况下也不会太多 - 如果有的话 - 的好处是用一类,特别是在频率与它们创建和丢弃是与它们将被简单地复制的频率的一个分数显著。 例如, Decimal是一个16字节的结构,所以拿着一百万Decimal值将需要16兆字节。 如果它是一个类,每个引用一个Decimal实例将需要4或8个字节,但每个不同实例可能会采取另一种20-32个字节。 如果一个人有很多大的数组的元素从一个小数量的不同的被复制Decimal情况下,类可以胜出,但在大多数情况下一个会更可能有一百万引用数组的百万不同实例Decimal ,这意味着该结构将胜出。

以这种方式使用的结构通常是唯一的好,如果从MSDN援引指南适用(尽管不变性原则是主要的事实,那就是没有任何办法,通过该结构的方法可以表明他们修改基础结构的结果)。 如果有任何的最后三个指导原则不适用,一个可能是关闭使用不可变类比结构更好。 如果第一个原则不适用,然而,这意味着一个不应该使用一个结构,但并不是说应该使用一个类代替。

在一些情况下,数据类型的目的是简单地用胶带固定在一起的一组变量,使得它们的值可以被绕过作为一个单元,但它们仍然在语义上为不同的变量。 例如,很多方法可能需要绕过代表3D协调三大浮点数组。 如果想画一个三角形,它的很多更方便地传递三个Point3d参数超过九浮点数。 在许多情况下,这种类型的目的不是传授任何特定领域的行为,而是简单地提供通过周围的事物方便的手段。 在这种情况下,结构可以在类提供重要的性能优势,如果使用它们正确。 这是应该一个结构来表示类型的三个varaibles double用胶带固定在一起应该简单地有类型的三个公共字段double 。 这样的结构将允许有效地执行两种常见的操作:

  1. 给定一个实例,利用其状态的快照,因此实例可以在不干扰快照进行修改
  2. 鉴于其不再需要一个实例,以某种方式拿出一个实例,它是略有不同

不可变的类类型允许在固定的费用,无论由类所保持的数据的量的执行的第一,但它们是在第二低效率的。 执行所述第一操作时的变量应该代表的数据量越大,不可变的类类型与结构的优点,并且在执行所述第二时暴露的场结构的优点越大。

可变类类型可以是在场景中有效,其中所述第二操作支配,并且所述第一,需要很少如果有的话,但它可以是困难的一个目的是暴露在一个可变类对象的当前值没有物体本身暴露于外部修改。

请注意,根据使用模式,大的暴露场结构可能比任一不透明结构或类的类型更有效。 结构大于17个字节往往比规模较小的效率较低,但他们仍然可以远远大于类更有效。 此外,传递一个结构作为成本ref参数不依赖于它的大小。 大结构是低效的,如果一个通过属性访问他们,而不是字段,按值传递它们不必要等,但如果一个人小心地避免冗余“复制”操作,也有使用模式那里是阶级与无盈亏平衡点结构 - 结构只会有更好的表现。

有些人可能会在恐怖的想法一类具有暴露领域退缩,但我会建议,如结构我描述不应该想那么多了,作为本身就是一个实体,而是写着的东西的延伸还是这样写吧。 例如:

public struct SlopeAndIntercept
{
   public double Slope,Intercept;
}
public SlopeAndIntercept FindLeastSquaresFit() ...

其中将要执行一串点的最小二乘拟合代码将必须做的工作的一个显著量找到任一斜坡或所得线的Y轴截距; 发现两者不会花费更多。 它调用的代码FindLeastSquaresFit方法很可能会希望有一个可变斜率,而在另一个拦截。 如果这样的代码呢:

var resultLine = FindLeastSquaresFit();

其结果将是有效地创建两个变量resultLine.SloperesultLine.Intercept该方法可以操纵它认为合适的。 领域的resultLine并不真正属于SlopeIntercept ,也不FindLeastSquaresFit ; 他们属于一个声明码resultLine 。 从如果该方法被用来作为的情况稍有不同:

double Slope, Intercept;
FindLeastSquaresFit(out Slope, out Intercept);

在这种情况下,这将是明确表示,立即在函数调用下面,两个变量的方法所赋予的涵义,但在其他任何时候它们的意义将取决于还有什么方法跟他们一样。 同样地,对于上述结构的字段。

有一些情况下它可能是更好的使用不可变类返回的数据,而不是透明的结构。 除此之外,使用类将使它更容易为一个返回函数的未来版本Foo返回的东西,其中包括更多的信息。 在另一方面,存在其中代码将期待处理一组特定的离散的东西,并改变一套东西将从根本上改变什么客户都用它做很多的情况。 例如,如果一个人有一堆代码,用(X,Y)点的交易,增加了“Z”坐标将需要代码重写,并没有什么“点”式可以做到减轻。



文章来源: Immutable class vs struct