关于.NET:什么时候应该使用结构而不是类?

关于.NET:什么时候应该使用结构而不是类?

When should I use a struct instead of a class?

msdn说当需要轻量级对象时应该使用结构。当结构比类更可取时,是否存在其他方案?

有些人可能忘记了:

  • 结构不能有方法。
  • 无法继承结构。
  • 我理解结构和类之间的技术差异,我只是不知道何时使用结构。


    msdn有答案:在类和结构之间进行选择。

    基本上,这一页给了你一个4项的清单,并说除非你的类型符合所有的标准,否则就使用一个类。

    Do not define a structure unless the
    type has all of the following
    characteristics:

    • It logically represents a single value, similar to primitive types
      (integer, double, and so on).
    • It has an instance size smaller than 16 bytes.
    • It is immutable.
    • It will not have to be boxed frequently.

    我很惊讶我没有读到之前的答案,我认为这是最重要的方面:

    当我需要一个没有标识的类型时,我使用结构。例如,三维点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public struct ThreeDimensionalPoint
    {
        public readonly int X, Y, Z;
        public ThreeDimensionalPoint(int x, int y, int z)
        {
            this.X = x;
            this.Y = y;
            this.Z = z;
        }

        public override string ToString()
        {
            return"(X=" + this.X +", Y=" + this.Y +", Z=" + this.Z +")";
        }

        public override int GetHashCode()
        {
            return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);
        }

        public override bool Equals(object obj)
        {
            if (!(obj is ThreeDimensionalPoint))
                return false;
            ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;
            return this == other;
        }

        public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
        {
            return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;
        }

        public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
        {
            return !(p1 == p2);
        }
    }

    如果您有这个结构的两个实例,那么您不关心它们是内存中的单个数据块还是两个。你只关心他们所拥有的价值。


    比尔·瓦格纳在他的书《有效的C》(http://www.amazon.com/effective specific ways improve your/dp/0321245660)中对此有一章。他的结论是,使用以下原则:

  • Is the main responsability of the type data storage?
  • Is its public interface defined entirely by properties that access or modify its data members?
  • Are you sure your type will never have subclasses?
  • Are you sure your type will never be treated polymorphically?
  • If you answer 'yes' to all 4 questions: use a struct. Otherwise, use a
    class.


    当需要值类型语义而不是引用类型时,请使用结构。结构是按值复制的,所以要小心!

    另请参见前面的问题,例如

    .NET中的struct和class有什么区别?


    我会在以下情况下使用结构:

  • 对象应该是只读的(每次传递/分配一个结构时,它都会被复制)。当涉及多线程处理时,只读对象非常好,因为在大多数情况下,它们不需要重新请求锁定。

  • 物体很小,寿命很短。在这种情况下,很有可能将对象分配到堆栈上,这比将其放在托管堆上要高效得多。更重要的是,一旦对象超出其作用域,它所分配的内存就会被释放。换句话说,垃圾收集器的工作更少,内存使用效率更高。


  • 如果使用:

    • 它的身份很重要。当按值传递给方法时,结构被隐式复制。
    • 它将占用大量的内存。
    • 它的字段需要初始值设定项。
    • 您需要从基类继承。
    • 你需要多态行为;

    在以下情况下使用结构:

    • 它将像一个基元类型(int、long、byte等)一样工作。
    • 它必须有很小的内存占用。
    • 您正在调用一个P/Invoke方法,该方法要求通过价值。
    • 您需要减少垃圾收集对应用程序性能的影响。
    • 其字段只需初始化为其默认值。对于数值类型,该值为零;对于布尔类型,该值为假;对于引用类型,该值为空。
      • 注意,在C 6.0中,结构可以有一个默认的构造函数,用于初始化结构的字段转换为非默认值。
    • 不需要从基类继承(ValueType除外,从该类继承所有结构都继承)。
    • 你不需要多态行为。


    当我想将一些值组合在一起以便从方法调用中传递内容时,我总是使用结构,但在读取这些值之后,我不需要将其用于任何内容。就像保持清洁一样。我倾向于把结构中的事物看作是"一次性的",而把类中的事物看作是更有用和"实用的"。


    如果一个实体是不可变的,那么使用结构或类的问题通常是性能问题而不是语义问题。在32/64位系统上,无论类中的信息量如何,类引用都需要存储4/8字节;复制类引用将需要复制4/8字节。另一方面,每个不同的类实例除了包含的信息和对它的引用的内存开销外,还将有8/16字节的开销。假设需要一个由500个实体组成的数组,每个实体包含四个32位整数。如果实体是结构类型,则无论500个实体是否都相同、都不同或介于两者之间,数组都将需要8000个字节。如果实体是类类型,500个引用的数组将占用4000个字节。如果这些引用都指向不同的对象,那么每个对象将需要额外的24个字节(所有500个对象需要12000个字节),总共需要16000个字节——是结构类型存储成本的两倍。另一方面,代码创建了一个对象实例,然后复制了对所有500个数组槽的引用,该实例的总开销为24字节,数组的总开销为4000字节,总共4024字节。一大笔节省。很少有情况能像最后一种情况一样有效,但在某些情况下,可能可以将一些引用复制到足够的阵列插槽中,以使这种共享变得有价值。

    如果实体应该是可变的,那么是否使用类或结构的问题在某些方面更容易处理。假设"thing"是一个结构或类,它有一个名为x的整型字段,并执行以下代码:

    1
    2
    3
    4
      Thing t1,t2;
      ...
      t2 = t1;
      t2.x = 5;

    是否希望后一个语句影响T1.X?

    如果事物是一个类类型,T1和T2是等价的,这意味着T1.x和T2.x也是等价的。因此,第二条语句将影响T1.X。如果事物是结构类型,T1和T2将是不同的实例,这意味着T1.X和T2.X将引用不同的整数。因此,第二个语句不会影响T1.X。

    可变结构和可变类具有根本不同的行为,尽管.NET在处理结构突变时有一些奇怪之处。如果需要值类型行为(即"t2=t1"将数据从t1复制到t2,同时将t1和t2保留为不同的实例),并且如果可以使用.NET对值类型处理中的怪癖,请使用结构。如果你想要值类型语义,但是.NET的怪癖会导致你的应用程序中的值类型语义被破坏,那么就使用一个类并含糊不清。


    除此之外,上述优秀答案:

    结构是值类型。

    它们永远不会被设置为零。

    将结构设置为"无",将其所有值类型设置为其默认值。


    正如@simon所说,结构提供了"值类型"语义,因此如果您需要类似于内置数据类型的行为,请使用结构。因为结构是通过copy传递的,所以您需要确保它们的大小很小,大约16个字节。


    当你真的不需要行为,但你需要比简单的数组或字典更多的结构。

    跟进这就是我对结构的一般看法。我知道他们可以有方法,但我喜欢保持这种整体的精神差异。


    结构在堆栈上,而不是堆上,因此它们是线程安全的,在实现传输对象模式时应该使用,您永远不希望使用堆上的对象,它们是易失的,在这种情况下,您希望使用调用堆栈,这是使用结构的基本情况,我对这里所有的答案感到惊讶,


    隐马尔可夫模型。。。

    我不会将垃圾收集用作支持/反对使用结构与类的参数。托管堆的工作原理与堆栈非常相似——创建对象只需将其放在堆的顶部,这几乎与在堆栈上分配一样快。另外,如果一个对象是短期的,并且在GC循环中不存在,那么释放是免费的,因为GC只使用仍然可以访问的内存。(搜索msdn,有一系列关于.NET内存管理的文章,我懒得去挖掘它们)。

    大多数时候,我使用一个结构时,我会因为这样做而自责,因为后来我发现使用引用语义会使事情变得简单一点。

    不管怎样,上面发表的msdn文章中的这四点似乎是一个很好的指导方针。


    我认为最好的答案就是在需要属性集合时使用struct,在需要属性和行为集合时使用class。


    推荐阅读