什么时候使用struct?

何时应该在C#中使用struct而不是class? 个人概念模型是当项只是值类型的集合时使用结构。 一种逻辑上将它们组合在一块儿造成一个有凝聚力的总体的方法。 web

我在这里遇到了这些规则: 数组

  • 结构应该表示单个值。
  • 结构应具备小于16个字节的内存占用。
  • 建立后不该更改结构。

这些规则有效吗? 结构在语义上意味着什么? 缓存


#1楼

C#或其余.net语言中的结构类型一般用于保存应该像固定大小的值组同样的事物。 结构类型的一个有用方面是结构类型实例的字段能够经过修改其所在的存储位置来修改,而不是以其余方式。 能够以这样的方式对结构进行编码:改变任何字段的惟一方法是构造一个全新的实例,而后使用结构赋值经过用新实例中的值覆盖它们来改变目标的全部字段,可是除非struct没有提供建立其字段具备非默认值的实例的方法,不然若是struct自己存储在可变位置,则其全部字段都是可变的。 数据结构

请注意,若是结构包含私有类类型字段,而且将其本身的成员重定向到包装类对象的成员,则能够设计一种结构类型,使其基本上表现得像类类型。 例如, PersonCollection可能提供属性SortedByNameSortedById ,这两个属性都包含对PersonCollection (在其构造函数中设置)的“不可变”引用,并经过调用creator.GetNameSortedEnumeratorcreator.GetIdSortedEnumerator实现GetEnumerator 。 此类结构的行为与PersonCollection的引用很是PersonCollection ,只是它们的GetEnumerator方法将绑定到PersonCollection不一样方法。 也能够有一个结构包装一个数组的一部分(例如,一个能够定义一个ArrayRange<T>结构,它将保存一个名为ArrT[] ,一个int Offset和一个int Length ,带有一个索引属性,对于一个索引idx在0到Length-1的范围内,将访问Arr[idx+Offset] )。 不幸的是,若是foo是这种结构的只读实例,当前的编译器版本将不容许像foo[3]+=4; 由于他们没法肯定此类操做是否会尝试写入foo字段。 wordpress

也能够设计一个结构,使其表现得像一个值类型,它保存一个可变大小的集合(每当结构都会被复制时),但惟一的方法就是确保没有对象。 struct持有一个引用将暴露于任何可能会改变它的东西。 例如,能够有一个相似于数组的结构,它包含一个私有数组,其索引的“put”方法建立一个新数组,其内容与原始数组的内容相似,除了一个更改的元素。 不幸的是,使这种结构有效地执行可能有些困难。 虽然有时结构语义能够很方便(例如,可以将相似数组的集合传递给例程,而调用者和被调用者都知道外部代码不会修改集合,可能比要求调用者和调用者更好。 callee防护性地复制他们给出的任何数据,类引用指向永远不会变异的对象的要求一般是很是严格的约束。 函数


#2楼

.NET支持value typesreference types (在Java中,您只能定义引用类型)。 reference types实例在托管堆中分配,而且在没有对它们的未完成引用时进行垃圾回收。 另外一方面, value types实例在stack中分配,所以一旦其范围结束,就会回收分配的内存。 固然, value types经过值传递, reference types经过引用传递。 除System.String外,全部C#原始数据类型都是值类型。 性能

什么时候使用struct over class, 测试

在C#中, structsvalue types ,类是reference types 。 您可使用enum关键字和struct关键字在C#中建立值类型。 使用value type而不是reference type将致使托管堆上的对象更少,从而致使垃圾收集器(GC)上的负载更少,GC周期更少,从而提升性能。 可是, value types也有其缺点。 传递一个大struct确定比传递引用更昂贵,这是一个明显的问题。 另外一个问题是与boxing/unboxing相关的开销。 若是您想知道boxing/unboxing意味着什么,请按照这些连接获取有关boxingunboxing说明。 除了性能以外,有时候你只须要类型来得到值语义,若是你只有reference types ,那么实现它将很是困难(或难看)。 您应该只使用value types ,当您须要复制语义或须要自动初始化时,一般在这些类型的arrays中。 this


#3楼

C#语言规范编码

1.7结构

与类同样,结构体是能够包含数据成员和函数成员的数据结构,但与类不一样,结构体是值类型,不须要堆分配。 结构类型的变量直接存储结构的数据,而类类型的变量存储对动态分配的对象的引用。 结构类型不支持用户指定的继承,而且全部结构类型都隐式继承自类型对象。

结构对于具备值语义的小型数据结构特别有用。 复数,坐标系中的点或字典中的键值对都是结构的好例子。 对小型数据结构使用结构而不是类可使应用程序执行的内存分配数量产生很大差别。 例如,如下程序建立并初始化100个点的数组。 将Point实现为类,实例化101个单独的对象 - 一个用于数组,一个用于100个元素。

class Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

class Test
{
   static void Main() {
      Point[] points = new Point[100];
      for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
   }
}

另外一种方法是使Point成为一个结构。

struct Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

如今,只实例化一个对象 - 数组的对象 - 而且Point实例以串联方式存储在数组中。

使用new运算符调用Struct构造函数,但这并不意味着正在分配内存。 结构构造函数只是返回结构值自己(一般在堆栈的临时位置),而不是动态分配对象并返回对它的引用,而后根据须要复制该值。

对于类,两个变量能够引用同一个对象,所以对一个变量的操做可能会影响另外一个变量引用的对象。 对于结构体,每一个变量都有本身的数据副本,而且一个变量不可能影响另外一个变量。 例如,由如下代码片断生成的输出取决于Point是类仍是结构。

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

若是Point是一个类,则输出为20,由于a和b引用同一个对象。 若是Point是一个结构,则输出为10,由于a到b的赋值会建立该值的副本,而且此副本不受随后的ax分配的影响

前面的例子强调告终构的两个局限性。 首先,复制整个结构一般比复制对象引用效率低,所以对于结构而言,赋值和值参数传递可能比使用引用类型更昂贵。 其次,除了ref和out参数以外,不可能建立对结构的引用,结构排除了它们在许多状况下的使用。


#4楼

Struct可用于提升垃圾收集性能。 虽然您一般没必要担忧GC性能,但有些状况下它可能会成为杀手。 就像低延迟应用程序中的大缓存同样。 请参阅此帖子以获取示例:

http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/


#5楼

个人规则是

1,始终使用课程;

2,若是存在任何性能问题,我会尝试根据@IAbstract提到的规则将某些类更改成struct,而后进行测试以查看这些更改是否能够提升性能。

相关文章
相关标签/搜索