排队问题

时间:2010-07-28 15:29:00

标签: c# .net

在我们的应用程序中,我们有一个Queue,其定义如下:

private static Queue RawQ = new Queue();

然后将两种不同类型的对象放在队列中,一种是来自类(class A)的对象,一种是来自结构(struct B)的对象。

当我们处理来自队列的数据时,我们使用typeof来检查队列中的项目属于哪种类型(A类或结构B)。

我的问题:

  1. 对于来自A类的对象,只将它们的引用复制到Queue,对于struct B中的对象,它们的值被复制到Queue,对不对?
  2. 对于队列,一些项是小的引用,一些项是更大的值(大约408字节)。如果队列不小,这会浪费很多内存空间吗?
  3. 你有更好的方法做同样的事吗?
  4. 感谢,

4 个答案:

答案 0 :(得分:3)

  

对于来自A类的对象,只有它们的引用被复制到Queue,而对于struct B中的对象,它们的值被复制到Queue,对吗?

正确。实际上,当您向队列添加结构B时,它首先被装箱。换句话说,您的B实例被复制到托管堆上,并且对该副本的引用被放入队列中。

  

对于队列,有些项是小的引用,有些项是更大的值(大约408字节)。如果队列不小,这会浪费很多内存空间吗?

可能 - 装箱B实例需要一份副本,它使用的内存比没有复制的内存多。这取决于原件会发生什么。

对于.NET结构,408字节非常大;一般的经验法则是structs shouldn't be bigger than 16 bytes。原因与此类似:大型结构由于复制和装箱而引入开销。

  

你有更好的方法来做同样的事情吗?

我首先要问B是否需要成为一个结构。另一个经验法则(我的,这次):您可能不需要在.NET代码中使用结构。

答案 1 :(得分:2)

  

1.对于来自A类的对象,只将其引用复制到Queue和   对于来自struct B的对象,它们的值   被复制到队列,对吗?

这是正确的。除了值类型将被装箱。

  

2.对于队列,有些项目是小而有些的参考   项目是更大的值   (约408字节)。这会浪费   如果队列不是,则有很多内存空间   小?

这大多是正确的。拳击将添加另外8个字节(同步块为4个字节,类型信息为4个),因此对于较小的结构而言,这是一个无关紧要的结构,但是对于较小的结构,这将代表较大的比率。

  

3.你有更好的方法做同样的事情吗?

最好的办法是将大型结构转换为类。知道何时根据大小选择结构或类没有硬性规则,但32字节似乎是一个常见的阈值。当然,根据您是否真的需要值类型语义,您可以轻松地证明较大的结构,但408字节可能超出该阈值。如果类型确实需要值语义,那么可以使它成为一个不可变类。

您可以进行的另一项更改是使用通用Queue类。值类型不会像普通Queue那样装箱。但是,即使使用通用版本,您仍然会复制该大型结构。

答案 2 :(得分:2)

来自C# spec

  

由于结构不是引用类型,   这些操作得以实施   不同的结构类型。当一个   结构类型的值转换为   类型对象或接口类型   这是由结构实现的,a   拳击行动发生。

因此,回答1)队列包含盒装结构,而不是实际的结构值。

2)的答案不在于此,盒装结构和引用在队列的实际分配中具有相同的大小。

对于3),我需要更多信息。最好在队列中具有相同的类型,并且具有以适当的方式由类和结构处理的多态操作。过多的case语句和typeof()调用表明你的程序比面向对象更程序化。也许这就是你想要的,但C#针对OO方法进行了优化。

答案 3 :(得分:1)

我试图仔细检查这个,但这就是我所相信的事情:

System.Collections.Queue类包含一个类型为Object的集合,它是一种引用类型。因此,当您将Struct的实例传递给队列时,它会被装箱作为对象。这会在堆上创建一个副本,并提供一个引用指针(这是Queue看到的)。所以,Queue本身并没有变得太大,但是如果你正在进行大量的这些操作,那么你最终会(根据微软的话)在内存和性能上击中装箱/拆箱。

有关详情,请参阅C# Language Specification