【问题标题】:Storing reference types in Struct在 Struct 中存储引用类型
【发布时间】:2012-02-16 15:46:38
【问题描述】:

假设我在 c# 中有一个非常简单的结构

   public struct foo{
   public  int a{get;set;}
   public  int b{get;set;}
   public  int c{get;set;}
   public  int d{get;set;}
   public  string hello {get;set;}
   }

我认为上述方法比使用class 更“有效”?

在什么时候,如果我继续添加字符串属性,是时候将结构转换为类了吗?

编辑:我计划将这个结构传递给一个以 GDI 为中心的应用程序。 我一直认为结构在主要处理值类型时性能更高。

【问题讨论】:

标签: c# struct


【解决方案1】:

我认为上述方法比使用类更“有效”?

绝对不是。 这是一个糟糕的结构。结构应该很小;不超过参考文件大小的四倍;您的结构是 32 位机器上五个引用的大小。结构应该代表;这似乎不代表任何价值。结构应该是不可变的;这充满了可变性。

在什么时候,如果我继续添加字符串属性,是时候将结构转换为类了吗?

将此结构转换为类的关键在于您创建它的那一刻。它本来就不应该是一个结构。您的默认假设应该是类足够好;仅当您有证据表明这样做可以解决您实际遇到的问题时才使用结构。

我曾计划围绕一个以 GDI 为中心的应用程序传递这个结构。我一直认为结构在主要处理值类型时性能更高。

在极少数情况下,结构比类更有效:当它们不可变、表示并且是由其他值组成,例如整数。在其他情况下,它们的效率较低:因为结构是按值复制的,所以大型结构的使用速度可能比引用慢得多。因为结构是按值复制的,所以可变结构会导致错误,因为当您认为您正在改变变量时,您会改变副本。因为结构是按值复制的,所以它们应该具有值的语义,而不是引用。并且因为只包含其他结构的结构可以被垃圾收集器完全跳过,所以结构只有在不包含引用时才能更有效地进行清理。

在任何情况下,判断某项是否更有效的方法是尝试两种方法并根据您的性能目标衡量其性能使用科学。设定一个目标并衡量它。使用结构“因为我认为它们更有效”是在传闻的基础上做出技术决策。 衡量,然后你就会知道什么更有效。

【讨论】:

  • 感谢 Eric 的详细回复。您对 System.Drawing.Rectangle 感觉如何?虽然这个结构体不包含引用类型,但它确实包含大量的值类型。
  • @maxp:我不想做一个比这大得多的结构,而且它是可变的事实令人烦恼。但是,可以说它的行为确实像一个值。此外,可能会在数组中创建大量矩形,因此它们不太可能被“自己”复制很多。矩形通常会通过传递一些包含矩形的引用类型来传递。任何涉及图形的东西的性能要求都是相当大的;你想避免收集压力。如果通过测量证明是合理的,则将其设为结构是一个合理的选择。
  • 结构应该很小;不超过参考文件大小的四倍; 任何描述这句话的链接? 您的默认假设应该是课程足够好;仅当您有证据表明这样做可以解决您实际遇到的问题时才使用结构。您希望在哪种情况下推荐结构?
  • @PankajGarg:回复:你的第一个要求:如果你不相信我的话,那你为什么要接受别人的呢?假设我告诉你读 Cwalina 的书;你为什么不直接向寻求参考呢?回复:你的第二个问题:当你表示一个小的不可变值时使用结构,最好由其他值组成
  • 天啊。现在很想反驳我的回答。
【解决方案2】:

如果您需要类提供的增强功能,例如继承,请切换。如果没有,结构可能会“更轻”一些,但除非您预计会出现一些性能问题,例如在具有大量迭代的紧密循环内进行垃圾收集,否则无论何时需要使用 ref进行修改等的方法可能会产生不必要的工作。 (尽管在那个例子中,销毁具有引用类型属性的结构也会导致 GC)。

实际结果是:使用结构还是类取决于您的用例,而不是您拥有的属性数量。

要更好地解释类和结构之间的差异以及相对优势和劣势,see this MSDN article

对于 Eric Lippert 关于垃圾收集、结构和类的出色说明,see his response to this question

【讨论】:

  • +1 对于这个答案的大部分。不过,我想知道,您是否可以扩展结构可以“更轻”一点的想法?我认为这就是 OP 得到他的想法的地方,即它们更有效,但是有四个 int 的结构在许多方面已经比一个类“更重”了。
  • @StriplingWarrior 好问题;其中一件是我不久前捡到的,我不记得是从哪里来的。我会阅读并更新我的答案。
  • 从活动根集合中删除结构本身永远不会触发 GC(除非有人故意在框架中编写代码来执行此操作,否则会很疯狂)。触发GC的是长寿命存储上的实例分配(当您想放置它的任何地方都没有空间时)。
  • @StriplingWarrior:每个类实例在 32 位机器上至少有 12 字节的开销,或在 64 位机器上的 24 字节开销。如果一个人要创建一种类型的新实例,就像一个人要传递它们一样多,那么结构通常会比类等价物更轻。
【解决方案3】:

我认为这不是你不断添加字符串的情况,而是你想对对象做什么的情况。结构是值类型,类是引用类型,据我了解(在内存堆/堆栈分配方面)结构有一些性能提升,但我认为最终这取决于您对对象所做的事情。

认为我曾经读到过,结构体非常适合短期内的许多对象,但我可能是错的。

【讨论】:

    【解决方案4】:

    如果有人说:

    富 x[100]; 富[1] = 富[2]; foo[1].a += 5;

    后一种说法是否会影响foo[2].a?如果foo 是struct 类型,则不会;如果它是一个类类型,它会。

    与 Lippert 先生所说的相反,您的类型作为结构体可能还不错,但有几点需要注意:

    1. 结构应该直接公开它们的字段,而不是通过属性,除非属性中包含一些重要的逻辑。
    2. 传递大于 16 字节的值结构比传递较小的结构要慢一点,因为 .net 对较小的结构有特殊处理。但是,通过 ref 传递不受结构大小的影响。
    3. 包含 mutable 引用类型的可变结构通常表现出语义,这是可变值语义和可变引用语义之间的奇怪交叉。但是,对于保存不可变引用类型(如 `string`)的结构来说,这不是问题。
    4. 结构类型的属性通常很难使用。引用类型的只读属性有时更容易使用,但是用同样保存 20 字节数据的类替换将保存 20 字节数据的结构类型,几乎可以使 32 位系统上的存储需求翻倍,或者在 64 位系统上是两倍多。

    可变值类型语义很有用。较旧的 C# 编译器(多年前)存在一些问题,有些人希望阻止人们学习理解它们,但这些并不是在适当的情况下不使用它们的好理由。

    【讨论】:

      【解决方案5】:

      请记住,您在结构中声明的任何内容都是默认公开的。如果您希望隐藏数据,则必须使用类。除此之外,OOP 的所有典型特性都在 struct 中不可用。

      仅供参考:Class (computer programming)

      【讨论】:

      • 结构受制于与类相同的访问修饰符,它们的属性也是如此。将所有实体明确声明为公共/私有/内部等也是一个好主意。
      • 这是一个关于 C# 的问题,而不是 C++。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-11
      • 2011-01-16
      • 1970-01-01
      • 2021-12-08
      • 2019-03-07
      • 2021-11-01
      • 1970-01-01
      相关资源
      最近更新 更多