【问题标题】:C# Struct usage tips?C# Struct 使用技巧?
【发布时间】:2010-10-13 00:58:54
【问题描述】:

我非常喜欢使用结构。

那么在阅读了这个article 之后,我还有什么其他的顾虑要反对一直使用它们吗?

另见:

【问题讨论】:

  • 这是一个链接到另外三个 dup 的 dup...
  • 我希望将 Sasha 的编辑标记为冒犯!!
  • 我添加了一些非常相似的参考资料。我没有称它们为重复,因为这不是完全相同的问题,但它足够接近,我投票结束。
  • @Dana:C++ 问题的链接无关紧要。 C++ 中的结构与 C# 中的结构完全不同。

标签: c# data-structures


【解决方案1】:

你应该对结构做以下考虑:

  • 结构应该是不可变的(可变结构不直观且不可预测)
  • 结构总是有一个无法更改的默认(公共无参数)构造函数
  • 结构体大小不应超过 16 个字节
  • 应覆盖 Equals 和 GetHashCode 方法以获得更好的性能
  • 推荐实现IEquatable<T>接口
  • 还建议重新定义 == 和 != 运算符

【讨论】:

  • 限制大小为16字节的原因是什么?
  • 据我所知,16 个字节是对引用类型的引用的大小。因此,在传递结构或使用它们时,您不会获得任何内存好处。虽然我会说。我不明白所有这些你应该使用不可变结构的东西。结构只是结构,如果它们是您需要的,我认为它们是您应该使用的。
  • 可变结构:考虑以下可变结构struct Point { public int X, public int Y }。当它在集合中使用时(例如List<Point>)迭代和修改它不会更新集合中的值:foreach(var p in points /* points is Point[] */) { p.X = 3 },因为从集合中获取元素只会给你一个copy(或元素的克隆)。将结构值作为方法参数传递也是如此。
  • 嗨迈克尔,这并不是说我不理解结构的问题(如果你可以这样称呼的话)。您对我所说的是我认为的预期行为。因此,如果可以预期,我不会期望这些值会被修改,如果它们没有被修改也不会感到惊讶。
  • @Barry:假设 ClearX() 通过引用获取一个 Point 并清除 X 组件,而 PointList 是一个 List。即使编译器不允许写 PointList[5].X=0,它也会允许 ClearX(PointList[5]);然而,后面的代码不会产生任何有用的效果。恕我直言,预期的行为可能是首先禁止此类代码,但允许此类代码作为不知情的陷阱。
【解决方案2】:

我几乎从不定义自定义结构。 IMO,周围没有那么多自然值类型。

特别是,在定义可变结构之前,我会非常、非常仔细考虑,尤其是当它通过接口实现发生变异时。可变结构的行为方式完全出乎人们的意料,导致代码难以理解。

我认为值得从"Design Guidelines For Developing Class Libraries" 阅读"Choosing Between Classes and Structures"。

特别是:

不要定义结构,除非 类型具有以下所有内容 特点:

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

  • 它的实例大小小于 16 字节。

  • 它是不可变的。

  • 不必经常装箱。

您真的经常开发具有所有这些特征的类型吗?

【讨论】:

    【解决方案3】:

    它们不像类那样适合面向对象的编程范式。它们适用于小型数据结构,但除此之外我还使用类。

    【讨论】:

      【解决方案4】:

      我认为结构的主要目的 - 只保留变量类型。如果你将一些类保留在结构中 - 你错了。

      【讨论】:

        【解决方案5】:

        问自己以下有关您正在使用结构建模的数据集的问题:

        • 可能需要任何 获取/设置逻辑?
        • 可能需要存储任何逻辑 是数据特有的吗?
        • 可能需要继承另一个集合 数据?
        • 可能需要另一组数据 继承这个?

        我认为,如果您可以对所有这些问题都回答“不”,那么没有充分的理由不使用结构。我认为人们在结构足够好的某些情况下使用静态子类。

        【讨论】:

        • 您能否详细介绍第 2 点?具有与之关联的逻辑的结构没有任何问题。
        • 嗯,这里的关键词是“地方性”与相关。我想这不是很清楚,但我的意思是:你的结构可能需要方法吗?我的意思是,类基本上是结构 + 地方性行为(嗯,好吧,它们不止于此,但这就是我的意思)
        【解决方案6】:

        类比结构更容易正确实现。如果你错误地实现了一个结构,它可能会给你一些意想不到的错误。

        结构不应大于 16 字节,否则您将失去大部分性能优势。

        结构旨在成为一种值类型,表示某种单一实体。

        结构应该是不可变的。这意味着您永远不会更改结构中的任何一个属性。如果你想要一个不同的结构值,你可以创建一个新值。

        【讨论】:

        • 其实结构体很容易正确实现。如果需要的是一组三个int,称为Moe、Larry、Curly、public struct Stooge { public int Moe, Larry, Curly; }。也许为了方便添加一个构造函数:public Stooge(int newMoe, int newLarry, int newCurly) {Moe = newMoe; Larry = newLarry; Curly = newCurly;}。试图将过多的逻辑放入结构中可能会出现问题,但有一个简单的解决方案:不要。试图用螺丝刀敲钉子的人会发现它是一把劣质的锤子,但这并不意味着螺丝刀不是一个好工具。
        • @supercat:这不是一个正确实现的结构,这只是一个应该是一个类的结构。一个结构应该代表一个实体,而不是多个实体的容器。
        • 如果 Stooge 的实例在它们所包含的值之外没有任何标识,这样 100 个 Stooge 实例的数组将包含与 300 个整数完全相同的信息,那么 Stooge 必须要么是结构体,要么是不可变类;虽然在某些情况下暴露字段结构是可用的,但不可变类会更好,但我认为这些是例外而不是规则。给定Stooges[] Stooge,如果Stooge 是一个类,如何在不影响AnyOtherStooge.Moe 的情况下更改Stooges[3].Moe?需要检查多少代码...
        • ...确认一个人这样做是正确的?如果Stooge 定义如上,Stooges[3].Moe = 27; 会很简单地完成这项工作;知道Stooges[] 是Stooge,而Moe 是Stooge 的字段就足以定义行为。如果Stooge 是一个不可变类,则可能可以执行Stooges[3] = Stooges[3].WithMoe(27);,但必须检查WithMoe 方法以确认它的作用,并检查整个Stooge 类以确保它确实是不可变的,并且WithMoe 正确复制其所有字段。这在某种程度上是一种改进?
        • 如果一个结构的行为不是一组用胶带绑定在一起的变量,那么它应该是不可变的。但是,如果需要一组变量用胶带粘在一起,方便时可以作为一组传递,也可以单独访问(例如点的坐标),使用组似乎更合理变量用胶带粘在一起(即暴露字段结构),而不是尝试创建一个有点像一个类的类,或者让一个结构陷入困境,使其表现得像这样一个类。
        【解决方案7】:

        结构的使用应仅限于您真正需要的只是一个小型数据结构。 (如您所读)。我只会真正将它们用于最小的数据结构,例如坐标、偏移量,有时还用于图形。

        在许多方面,您可以将 C# 中的结构视为按比例缩小的类。它们与类基本相同,但更多地是为您只想将一些数据组合在一起的情况而设计的。他们 在以下方面与类不同: 注意事项

        • 结构是值类型,而不是 引用类型。这意味着他们是 存储在堆栈中或在- 线(如果它们是另一个的一部分 存储在堆上的对象) 并且拥有相同的寿命 重新限制作为简单数据 类型。
        • 结构不支持继承。
        • 存在一些差异 构造函数为结构工作的方式。 特别是,编译器总是 提供默认的 no - 参数 构造函数,你不是 允许更换。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-10-09
          • 2011-01-21
          • 1970-01-01
          • 1970-01-01
          • 2011-12-08
          • 2010-11-17
          • 1970-01-01
          相关资源
          最近更新 更多