【问题标题】:struct - what is it for?结构 - 它是做什么用的?
【发布时间】:2009-12-28 18:40:46
【问题描述】:

我对@9​​87654322@ 类型有所了解。但我不明白:它是干什么用的?我什么时候用过?类、简单的值类型和枚举——这就是我所需要的。

有什么建议吗?

UPD:请!不要告诉我结构在堆栈中(我知道这一点:)。结构是干什么用的?

【问题讨论】:

  • 不是您问题的答案,但如果您确实使用结构,请避免使用可变结构:stackoverflow.com/questions/441309/why-are-mutable-structs-evil
  • 答案是它们是存储在堆栈中的值类型,而不是存储在堆中的引用类型。所以答案只是你说你不想要的答案。
  • 如果您知道“它在堆栈中”,那么您应该知道它们的用途。
  • 我的评论是讽刺的,暗示他应该知道“它在堆栈中”,他应该了解真正的含义,但是你的 cmets 真的很有趣..跨度>
  • +1:大多数是个好问题,因为至少有一半提交的答案有多个反对票;)

标签: c# struct


【解决方案1】:

如果你想要值类型语义,你可以选择一个结构。如果你想要引用类型的语义,你可以选择一个类。所有其他问题都是次要的。

【讨论】:

  • 这绝对是正确的(就像您在其他地方的出色评论一样),但可能没有帮助:我认为大多数会问这个问题的人显然不理解引用类型和值类型之间的区别语义。
  • 但问题是“结构 - 它有什么用?”不是“值类型和引用类型有什么区别”。如果发帖人想知道,那么他应该在评论中问它,或者更好的是,打开一个新问题。我相信很多人也很乐意为这个问题提供答案。
  • 这个答案当然是正确的,但如果你能解释这些差异会更好......其中有一些。
  • @Stan R.:正如我在对 Jeff Sternal 的回答中提到的,我认为最好在单独的问题中解决这个问题。 structs 和 classes 分别是 value-type 和 reference-type 语义类型的实现;他们没有定义概念。
  • @Jason。我同意 Jason,但我们不能像那样挑剔每一个问题。如果我们这样做了,我们将永远没有答案。虽然有很多 SO 问题可以回答这个确切的问题,但我不知道为什么还没有关闭。
【解决方案2】:

MSDN 提供指南:Choosing Between Classes and Structures:

如果类型的实例很小且通常寿命短或通常嵌入其他对象中,请考虑定义结构而不是类。

除非类型具有以下所有特征,否则不要定义结构:

  • 它在逻辑上表示单个值,类似于原始类型(整数、双精度、> 等)。
  • 它的实例大小小于 16 字节。
  • 它是不可变的。
  • 不必经常装箱。

【讨论】:

  • 我讨厌那个指南,因为它谈到了性能问题和结构/堆栈类/堆实现细节的废话。这些关注点与主要关注点相比,远远排在第二位:语义。
  • @Jason:实体分为三种类型:可变值类型、可变引用类型和不可变类型。唯一在语义上与引用类型不同的结构是可变结构。我个人认为可变值类型有时很有用,并且认为大多数关于它们的抱怨实际上是对 .net 如何处理它们的限制的抱怨(例如,无法指定自定义装箱运算符),但有些人讨厌它们。
【解决方案3】:

应该是结构的东西(因为它们是值):

  • struct Color
  • struct Point
  • struct Rectangle
  • struct GLVertex(包含位置、颜色、法线和纹理坐标)
  • struct DateTime

应该是类的东西(因为它们是你所指的东西):

  • class RandomGenerator
  • class Socket
  • class Thread
  • class Window

为什么? 考虑以下代码。

class Button
{
    public Point Location { get; set; }
}

class Program
{
    public static void Main()
    {
        var button = Util.GetButtonFromSomewhere();
        var location = button.Location;
        Util.DrawText("one", location);
        location.Y += 50;
        Util.DrawText("two", location);
        location.Y += 50;
        Util.DrawText("three", location);
    }
}

这将绘制 3 个垂直对齐的文本标签。但是如果 Point 是一个类,这也会移动按钮,这真是出乎意料:var location = button.Location 感觉它应该复制一个值,而不是引用!换句话说,我们将 Point 视为值类型而不是引用类型。这里的“值”用于数学意义上的。考虑数字 5,它是“某处”的抽象对象,您只需“引用”它即可。同样,一个点只是。它不会驻留在任何地方,我们无法更改它。因此我们选择把它做成一个结构体,这样它就有了用户期望的语义。

另一方面,我们可以有class Button { public Window Parent { get; set; } }。这里,Parent 是一个实体,所以我们用引用类型来表示它 - Window。使用myButton.Parent.Redraw(); 之类的代码可能很有意义。所以Window 应该是一个类。

到目前为止一切顺利。但这一切对你来说可能听起来太模糊了。您如何真正确定某事物“感觉”是参考还是价值?我的经验法则很简单:

Foo a = b; a.Mutate(); 应该怎么做?

  • 如果它看起来应该保持 b 不变,请将 Foo 设为结构。
  • 否则将其设为一个类。

在此处使用principle of least surprise

【讨论】:

  • 回答“struct vs class”问题的最佳方式通常是问你的“What should ...”问题,尽管我会用“a.x=5”替换“a.Mutate() "; 我还注意到 vb.net 和 C# 有一些令人讨厌的限制,即使某些东西应该具有值语义,也可能需要使用类(例如,使包含数组的结构的行为几乎是不可能的)像值类型)。
【解决方案4】:

简单的值类型最好通过结构来实现。

结构使用指南

建议您使用 满足任何类型的结构 以下标准:

* Act like primitive types.
* Have an instance size under 16 bytes.
* Are immutable.
* Value semantics are desirable.

您还必须了解,类实例是在堆上分配的。 struct - 是一种值类型 - 并在堆栈上分配。

【讨论】:

  • 好的。但是,如果使用 struct 的原因仅仅是内存优化,那么......你在使用反射吗?繁重的 SQL 查询?林克?我认为,这些事情中的任何一个都与结构的这种优化重叠。
  • 结构并不总是分配在堆栈上。而选择结构的主要原因与在堆栈上分配的实现细节无关。
  • 我并不是说结构只对内存优化有用。结构是创建值类型的理想工具,这就是我要说的。 Jason:你如何在 .NET 中创建一个在堆上分配的结构?
  • @Frederik Gheysels:盒装结构、作为类成员的结构以及因为它是匿名方法中的外部变量或迭代器块而被提升的结构都分配在堆上。
【解决方案5】:

首先您必须了解值类型和引用类型之间的区别。既然你说跳过那部分,我会假设你明白它是什么。

Struct 是一种值类型,您可以获得使用值类型所获得的所有特权。

  • 结构是按值传递的。当你做类似的事情时

    日期时间时间 = 新日期时间(); 日期时间 newTime = 时间; // 你没有参考时间 // 相反,您创建了一个新实例

  • 结构不是轻量级的类,它们可能有很多方法,看看DateTime 结构。

  • 结构的性能可能更轻,但并非一直如此。考虑将大型结构传递给方法。因为每次将结构体传递给方法时,结构体都是值类型,所以会创建结构体的新实例,因此每次都会复制结构体。如果你有一个相当大的结构,这将对性能造成更大的影响。

  • 您可能不得不偶尔对结构进行装箱和拆箱,因为它们是值类型。

简而言之,使用结构体来表示内存中的原子值。

【讨论】:

  • 如果不赞成票的人解释反对票的用途会很好吗?
  • 结构不是不可变的(但我没有反对)。见stackoverflow.com/questions/608542/immutability-of-structs
  • @Brian。谢谢,我宁愿有人向我指出这一点,而不是反对。我所说的不可变是指持有结构的变量是不可变的。显然,一旦创建结构,它就不是只读的,根据定义,它当然是可变的..除非你制作一个不可变的结构(我认为这是一个很好的做法)
【解决方案6】:

当您想要一个具有值(而不是引用)语义的“类”时,您可以使用结构。

【讨论】:

  • 假设你想要一个 Complex 类型来存储复数。复杂类型的行为应该与 int 完全相同 - 因为两者都是某些类型的数字。您不能将 Point 声明为类,因为类是引用类型。表达式“复杂 a = b”应该分配 b 的“a”,而不是 引用
【解决方案7】:

结构用于表示其身份由存储在其属性中的值而不是由 Id 或键定义的对象的对象。这些被称为“值类型”,而不是被称为“实体类型”的 tyo 对象,其身份会随着时间的推移而持续存在,并且不依赖于对象属性的值。

例如,Person 类(实体)具有从一个会话持续到另一个会话的身份,即使是年复一年,尽管 Person 的地址、电话号码、雇主等可能会从一个实例发生变化给另一个。如果您无意中在内存中同时有两个 Person 类的实例,它们代表相同的个人/实体,那么它们的属性具有相同的值很重要。

CalendarMonth 对象otoh,(一种值类型)仅具有由指定它是哪个日历月的值定义的标识...无论您可能使用多少个“2009 年 3 月”实例,它们都是可互换的,并且等价的。另一个示例可能是在税收计划中表示 FiscalYear 名称的对象。一个很好的例子是地址对象。 (这里不谈论地址与个人或企业或任何其他实体的关联,而只是地址本身)。更改地址的几乎任何属性都会使其成为不同的地址。一旦您在内存中创建了地址对象,它就可以与具有相同属性的所有其他地址对象等效且可互换。这就是为什么这些值类型通常应该是不可变的。当您需要一个时,使用您需要的属性值创建一个新的,并在使用完毕后将其丢弃。

【讨论】:

    【解决方案8】:

    如果你不知道为什么需要它,你可能不知道。

    struct 是值类型而不是引用类型。如果您不知道这意味着什么,那么您可能不需要它。

    【讨论】:

    • 但是我可能错过了什么吗?我可以在没有枚举的情况下进行编码,但我猜它会不太舒服。
    • stuct 和 enum 不是一回事。你为什么会提到他们?
    • @Mystere Man 他使用枚举作为您逻辑的反例。仅仅因为他不知道结构的用途并不意味着这些知识对他没有用处。同样,他可以在不了解枚举的情况下度过难关,但这种知识会让他的生活更轻松。
    • 我并不是说这些知识没有用,我的意思是他可能还没有达到结构和类之间的区别对他的决定很重要的理解水平。即使知道它们的区别,我在编程中也很少需要结构体。
    • MystereMan,我恭敬但强烈反对。如果你不知道结构和枚举之间的关系,那么也许你“不需要”使用其中任何一个......显然你不明白结构何时以及如何有用,或者你不会那么消极关于他们。
    【解决方案9】:

    示例:假设您想要一个数据类型来表示 X Y Z 处的坐标。您实际上不需要任何功能,只需要三个变量。一个结构会很好,一个类可能是矫枉过正。

    【讨论】:

    • 谢谢 :) 但是,现在我认为,这样的优化是没有用的:在使用反射和大型 SQL 查询时,谁真正关心这些小事情(ORM 的 3 个连接,哈哈 :)我只看到一件有用的事情——struct 不能为 NULL。
    • 为工作选择合适的工具也是一个问题。当我看到一个结构时,我立即知道对它有什么期望。对于像我这样的例子,一个简单的结构感觉更正确。
    【解决方案10】:

    实际上,我认为 struct 是 C 的遗产。我认为我们必须在任何情况下都使用它。也许有时你会觉得把东西放在栈上比放在堆上更有效;但由于 Java/C# 从来没有把效率作为第一立场,所以忽略它吧:) 这是我的看法。

    【讨论】:

      猜你喜欢
      • 2014-11-08
      • 2021-05-29
      • 1970-01-01
      • 2014-09-01
      • 2016-10-24
      • 1970-01-01
      • 2011-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多