为什么这是作为结构实现的?

Sim*_*itt 43 c# linq struct

在System.Data.Linq中,EntitySet<T>使用几个ItemList<T>结构,如下所示:

 internal struct ItemList<T> where T : class
  {
    private T[] items;
    private int count;
    ...(methods)...
  }
Run Code Online (Sandbox Code Playgroud)

(花了我比它应该发现的更长的时间 - 无法理解为什么实体字段EntitySet<T>没有抛出空引用异常!)

我的问题是,将它作为一个类的结构实现它有什么好处?

Mar*_*age 34

让我们假设您要存储ItemList<T>在数组中.

分配值类型数组(struct)将数据存储在数组中.另一方面ItemList<T>,如果是引用类型(class),则只有ItemList<T>对象的引用才会存储在数组中.实际ItemList<T>对象将在堆上分配.到达ItemList<T>实例需要额外的间接级别,因为它只是一个与长度相结合的数组,使用值类型更有效.

结构与类

在检查代码之后EntitySet<T>我可以看到没有涉及数组.但是,EntitySet<T>仍然包含两个ItemList<T>实例.由于ItemList<T>是一个struct用于这些实例的存储是在内部分配EntitySet<T>对象.如果使用了a class,那么EntitySet<T>将包含指向EntitySet<T>分别分配的对象的引用.

在大多数情况下,使用其中一个或另一个之间的性能差异可能并不明显,但开发人员可能决定将数组和紧耦合计数视为单个值,因为它似乎是最好的事情.

  • +1精彩的解释,完美展现! (2认同)
  • 喜欢图表!不幸的是,这里没有涉及ItemList <T>的数组,只有两个实例: (2认同)

Ric*_*key 9

对于像ItemList<T>我这样的小型关键内部数据结构,我们通常可以选择使用引用类型或值类型.如果代码写得很好,从一个代码切换到另一个代码是一个微不足道的改变.

我们可以推测一个值类型可以避免堆分配,而引用类型可以避免结构复制,因此它不会立即清楚,因为它在很大程度上取决于它的使用方式.

找出哪个更好的最佳方法是测量它.无论哪个更快,都是明显的赢家.我确信他们做了基准测试并且struct速度更快.在你完成这几次之后,你的直觉非常好,基准测试只是证实你的选择是正确的.


Ren*_*iuz 6

也许重要的是......从这里引用结构

因此,新变量和原始变量包含相同数据的两个单独副本. 对一个副本所做的更改不会影响另一个副本.

只是想,不要难以评判我:)

  • 好点子.但是,当计数与数组相关联时,计数会跟踪数组中正在使用的项目数.如果复制此结构并修改新结构中的计数,原始结构仍将引用相同的数组,但计数不会更新.所以对于结构的这种特殊用法,我很确定开发人员必须非常小心__not__来复制结构. (2认同)

Guf*_*ffa 5

使用结构实际上只有两个原因,那就是获取值类型语义,或者获得更好的性能.

由于struct包含一个数组,因此值类型语义不能很好地工作.复制结构时,您会获得该结构的副本count,但只能获得对数组的引用的副本,而不是数组中项目的副本.因此,每当复制结构时都必须特别小心,这样就不会得到不一致的实例.

因此,唯一剩下的有效理由是表现.每个引用类型实例都有一个小的开销,所以如果你有很多它们,那么可能会有明显的性能提升.

这种结构的一个很好的特性是你可以创建它们的数组,并且你得到一个空列表数组而不必初始化每个列表:

ItemList<string>[] = new ItemList<string>[42];
Run Code Online (Sandbox Code Playgroud)

由于数组中的项是零填充的,因此count成员将为零,items成员将为null.


Ton*_*ony 2

这里纯粹是猜测:

由于该对象相当小(只有两个成员变量),因此它是一个很好的候选对象, struct可以将其作为 a 传递ValueType

此外,正如 @Martin Liversage 指出的那样,通过成为 a,ValueType它可以更有效地存储在更大的数据结构中(例如作为数组中的项目),而无需拥有单个对象及其引用的开销。

  • @Jen:好的,但是将其作为 ValueType 传递有什么好处? (3认同)