【问题标题】:C# why need struct if class can cover it?如果类可以覆盖 C# 为什么需要结构?
【发布时间】:2010-02-19 21:13:47
【问题描述】:

只是想知道为什么我们需要 struct 如果 class 可以做所有 struct 可以甚至更多?我认为把值类型放在类中没有副作用。

编辑:看不到使用 struct 的任何强有力的理由

结构体类似于类,主要区别如下:

  • 结构是值类型,而 class 是一个引用类型。
  • 结构不支持继承 (除了隐含地派生自 对象)。
  • 一个结构可以有所有成员 类可以,但以下情况除外:
  • 无参数构造函数
  • 终结器
  • 虚拟会员

当需要值类型语义时,使用结构而不是类。结构体的好例子是数字类型,在这种类型中,分配复制值而不是引用更为自然。因为结构是值类型,所以每个实例不需要在堆上实例化对象。在创建一个类型的许多实例时,这可能很重要。

【问题讨论】:

标签: c# struct


【解决方案1】:

自定义值类型不是绝对必要的 - 例如,Java 没有它们。但是,它们仍然很有用。

例如,在Noda Time 中,我们非常广泛地使用它们,作为一种表示瞬间等事物的有效方式,而无需涉及对象的开销。

我不会说“class 可以做 struct 可以做的所有事情,甚至更多”——它们的行为不同,应该有不同的想法。

【讨论】:

  • 最近几天阅读类和结构之间的差异。可以肯定的是,我已经将注意力集中在大多数(全部?)细微差别上(“x”情况下的性能,“y”情况下的内存使用,等等,等等,等等)。这有点过于简单了,但是您会说以下陈述相当准确吗?除了 value-vs-ref 类型之外,类是具有额外功能但也有额外开销的结构。除非您需要额外的功能或计划作为参数传递(值与引用类型在这里发挥作用),否则结构使用的内存更少并且“性能”比类更好。
  • @AndrewSteitz:不,我不会那样说。 “值类型与引用类型”的主要区别......并且根据它们的使用方式,您最终可以使用引用类型比值类型少得多的内存。 (想象一个 32 字节的值,其中有 100 个相同的值……使用引用类型,您可以有 100 个对同一对象的引用,而值类型有 100 个 32 字节的副本。)
  • 非常感谢!我以为我已经把注意力集中在了这些差异上,但是您强调价值与参考是主要差异的重点告诉我,我应该多学习一点。
【解决方案2】:

class 有效时,为什么要使用struct?因为有时class 不起作用。

除了 Reed Copsey 提到的性能原因(简短版本:GC 需要跟踪的对象更少,让 GC 做得更好)之外,还有一个地方必须使用:P/Invoke 到需要按值结构或结构成员的函数。

例如,假设您想调用CreateProcess() 函数。进一步假设您想为CreateProcess()lpStartupInfo 参数使用STARTUPINFOEX 结构。

那么,STARTUPINFOEX 是什么?这个:

typedef struct _STARTUPINFOEX {
    STARTUPINFO                    StartupInfo;
    PPROC_THREAD_ATTRIBUTE_LIST    lpAttributeList;
} STARTUPINFOEX, *LPSTARTUPINFOEX;

注意STARTUPINFOEX 包含STARTUPINFO 作为其第一个成员。 STARTUPINFO 是一个结构体。

由于类是引用类型,如果我们这样声明对应的 C# 类型:

[StructLayout(LayoutKind.Sequential)]
class STARTUPINFO { /* ... */ }

class STARTUPINFOEX { public STARTUPINFO StartupInfo; /* ... */ }

相应的内存布局将是错误,因为STARTUPINFOEX.StartupInfo 将是一个指针(在 ILP32 平台上为 4 个字节),而不是一个结构(根据需要) ,在 ILP32 平台上大小为 68 字节)。

因此,为了支持调用接受任意结构的任意函数(这就是 P/Invoke 的全部意义所在),需要以下两件事之一:

  1. 完全支持值类型。这允许 C# 为 STARTUPINFO 声明一个值类型,它将具有正确的内存布局以进行封送处理(即 struct 支持,就像 C# 一样)。

  2. P/Invokeable 结构中的一些替代语法会通知运行时封送处理程序,该成员应该被布置为值类型而不是指针。

(2) 是一个可行的解决方案(并且可能在 Visual J++ 中的 J/Direct 中使用过;我不记得了),但鉴于正确的值类型更灵活,可以启用一些其他方式无法实现的性能优化,并在 P/Invoke 场景中合理使用,C# 支持值类型也就不足为奇了。

【讨论】:

    【解决方案3】:

    一般来说,使用一个类。

    只有在绝对需要值类型语义时才使用结构。

    【讨论】:

    • 结构类型不“只存在于堆栈中”。一个整数数组不在堆栈上。 int 类型的类字段不在堆栈上。 int 类型的静态字段不在堆栈上。迭代器块中的 int 类型的局部变量不在堆栈上。 int 类型的局部变量是匿名方法或 lambda 表达式的封闭外部变量不在堆栈上...结构类型通常 not 在堆栈上,所以说“他们可以只活在堆栈上”是完全误导的。结构的价值在于您可以以便宜的方式将它们大量放在堆上
    • 我没有说结构只存在于堆栈上,我说你应该只创建一个当你希望它只存在于堆栈上时。您所说的一切都是绝对正确的,但是如果该结构仅在封装在其他数据类型中时才存在,则它应该是一个类。仅当结构必须位于堆栈上时才创建结构。
    • 但是没有类型、结构或其他类型,必须存在于堆栈中。如果您需要的是必须存在于堆栈中,那么您要使用的语言是 C,而不是 C#。创建结构类型的原因是因为您想对逻辑上是值而不是引用的东西建模。您将作为实现细节的“生活在堆栈上”与“具有值语义”混为一谈。值类型无论在哪里都有值语义。
    • 我还注意到您犯了“在堆栈上等于快”的常见错误。值类型是按值复制的,这意味着即使是小的类型,在频繁复制时也可能比引用类型更昂贵;缓慢、频繁复制的负担超过了分配和释放效率的微小差异。值类型的性能优势不在于它们快速,而是它们可压缩。一百万个装箱的 int 比一百万个未装箱的 int 占用更多的空间。
    • @Lawnmower:没有结构体如何获得值类型语义?给定Point p1,p2; ...; p1=p2; ...;,除了将Point 设为结构之外,我怎么能对p1.X 进行更改而不影响P2.X
    【解决方案4】:

    出于性能原因,通常还需要结构。结构数组占用的内存要少得多,并且比对象引用数组获得更好的缓存一致性。如果您正在使用渲染系统之类的东西,并且需要生成 500 万个顶点,这一点非常重要。

    详情请见Rico Mariani's Performance Quiz + Answers

    【讨论】:

      【解决方案5】:

      结构之所以有用,仅仅是因为它们是按值传递的,这实际上在某些算法中很有用。这实际上是类不能做的事情。

      struct ArrayPointer<T>
      {
          public T[] Array;
          public int Offset;
      }
      

      您可以将此结构传递给一个方法,该方法可以根据自己的需要更改 Offset 的值。同时,一旦您从该方法返回,它的行为就好像 Offset 从未改变过一样。

      【讨论】:

      • 我不赞成这个例子,因为除非被调用的方法创建一个新数组,否则对数组本身的更改将对调用者可见。一个更好的例子是struct Point3d {public double X,Y,Z;},人们希望知道Point3d[100]; 将始终包含 100 个不同的 XYZ 三元组,而不是例如对一个常见实例的 100 次引用。
      【解决方案6】:
      【解决方案7】:

      对于一般的应用程序开发人员来说,使用类是常态。从表面上看,类使结构看起来没有必要,但当您深入挖掘细节时,它们实际上是完全不同的。

      请参阅here 了解结构和类之间的一些区别。

      最广为人知的区别是类是引用类型而结构是值类型。这很重要,因为它允许库开发人员控制如何使用数据类型的实例。

      【讨论】:

        【解决方案8】:

        1,性能。在某些情况下,使用结构我们会获得更好的性能。
        2、数据不变性。通过更改结构的某些属性,您将获得一个新结构。这在某些情况下非常有用
        3、更好地控制内存中的表示。我们可以准确地定义结构在内存中的位置,这使我们能够快速有效地序列化和反序列化一些二进制数据。

        【讨论】:

          【解决方案9】:

          与win32 API使用的底层数据结构进行互操作几乎是必须的。我想在.net中拥有它的一个很大的原因可能是由于这个原因。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-12-02
            • 1970-01-01
            • 2016-06-22
            • 1970-01-01
            • 1970-01-01
            • 2013-03-06
            • 1970-01-01
            相关资源
            最近更新 更多