【问题标题】:Why are System.Drawing Rectangle, Point, Size etc mutable structs and not classes?为什么 System.Drawing Rectangle、Point、Size 等是可变结构而不是类?
【发布时间】:2013-01-07 08:49:30
【问题描述】:

微软决定制作这些结构是有原因的吗?

这三个都是可变的。如果它们是不可变的,或者它们是引用类型,我会发现它们更容易处理。

如果有理由它们必须是结构,为什么它们是可变的?

【问题讨论】:

  • 您能否详细说明他们可变的structness 是如何导致您出现问题的?
  • @Damien_The_Unbeliever 例如:foreach (Point r in points) { r.X += 1; } 期望它们表现得像引用类型,但必须不断提醒自己它们不是。
  • @DominicKexel,这个问题是相关的,尽管接受的答案只与 WPF 有关。
  • @Rotem 这就是我没有投票关闭的原因:-)

标签: c# .net design-decisions


【解决方案1】:

为什么它们是结构体

价值语义
这些值的两个相同实例之间没有本质区别。任何具有坐标的Point[2,3] 等于具有相同坐标的任何其他点,就像任何两个具有相似值的ints 相等一样。 这符合设计指南:

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

性能

值类型的分配和释放成本更低。

通常需要创建这些值的许多实例。 struct 的创建成本更低,如果是本地值,则会在堆栈上创建,从而减轻 GC 的压力。

尺寸
让我们考虑一下这些值的大小:
Point:8 个字节
Size:8 个字节
Rectangle:16 个字节

对于PointSize,它们的大小与对64 位类实例的引用相同 系统。

引自微软指南:Choosing Between Classes and Structures

为什么它们是可变的

这些结构是完全可变的。这样做(违反指南)是为了提高性能,因为它避免了为修改操作创建新值的需要。

关于 cmets 中 OP 的代码示例:

Point[] points = new Point[] { new Point(0,0), new Point(1,1), new Point(2,2) };

foreach (Point p in points)
{
    p.X += 1; 
}

这个foreach 失败的唯一原因是因为p装箱object 以便提供迭代,而你Cannot modify the result of an unboxing conversion(感谢 Rajeev)迭代器按值返回数据,您只需更改值的 副本

这很好用:

for (int i = 0; i < points.Length; i++)
{
    points[i].X += 1;
}

【讨论】:

  • @Rotem Rectangle 根据该链接违反了四分之三的 Microsoft 准则。
  • @Andrew 我同意!但我想这在逻辑上是一个比其他点更重要的事实。
  • @Rotem 我明白你的意思。也许问题应该是为什么它们是可变的?
  • @JLRishe 不,它们是可变的。 Rectangle 例如有一个 Offset 方法来改变它的坐标。第三,对我来说,Rectangle 并不代表一个单一的值,但我认为这是主观的 :)
  • @Andrew 我的错。在那种情况下,我也觉得它们是可变的很奇怪。我已经撤销了我之前的评论。
【解决方案2】:

Microsoft 不需要将这些结构定义为一个类。

这些基本上都是小型结构。

如果这些被定义为class,对于Point 结构,相同的坐标可以引用内存中的不同对象。定义为struct,我们知道具有相同坐标的不同点之间没有区别。这意味着它们是值类型。值类型的分配几乎总是更便宜。看看它们的大小;

Point : 8 bytes
Size: 8 bytes
Rectangle: 16 bytes

谁想在每次创建Point(1,2) 时分配一个新的内存部分?

【讨论】:

    猜你喜欢
    • 2014-06-29
    • 2012-08-27
    • 2014-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多