2010-07-28 106 views
4

在我們的應用,我們有其被定義爲下面這樣的隊列:隊列問題

private static Queue RawQ = new Queue(); 

然後兩種不同類型的對象被放置到隊列中,一個是從一類(class A)和一個是對象來自結構體的對象(struct B)。

當我們處理來自Queue的數據時,我們使用typeof來檢查隊列中屬於哪個類型(A類或B類)的項目。

我的問題:

  1. 從A類對象,只有它們的引用複製到隊列中,並從結構B對象,它們的值複製到隊列,對嗎?
  2. 對於隊列,有些項目是小型參考,有些項目是更大的值(大約408字節)。如果Queue不小,這會浪費很多內存空間?
  3. 你有更好的方法來做同樣的事情嗎?

感謝,

回答

3

從A類對象,只有它們的引用複製到隊列中,並從結構B對象,它們的值複製到隊列,對嗎?

正確。實際上,當你將一個結構B添加到隊列中時,它首先被裝箱。換句話說,您的B實例被複制到託管堆中,並且將對該副本的引用放在隊列中。

對於一個隊列,有些項目是小的參考,有些項目是更大的值(大約408字節)。如果Queue不小,這會浪費很多內存空間?

可能 - 裝箱B實例需要一個副本,它使用更多的內存比不復制副本。這取決於原件發生了什麼。

對於.NET結構,408個字節非常大;一般的經驗法則是structs shouldn't be bigger than 16 bytes。原因與此類似:大型結構由於複製和裝箱而引入開銷。

你有更好的方法來做同樣的事情嗎?

我會問B是否需要首先是一個結構。另一個經驗法則(這次是我的):你可能不需要在.NET代碼中需要一個結構。

1

我試圖仔細檢查這一點,但這裏是我相信的情況:

的System.Collections.Queue類持有Object類型的集合是引用類型。因此,當你將一個Struct的實例傳遞給你的隊列時,它會被作爲一個對象裝箱。這會在堆上創建一個副本,並提供一個引用指針(這是隊列所看到的)。所以,Queue本身並不會太大,但是如果你正在做很多這些操作,那麼最終(根據微軟),內存和性能會超過拳擊/拆箱。

查看C# Language Specification瞭解更多信息。從A級

+0

是的,這是非常接近。 – 2010-07-28 17:40:05

2

1.對於對象,只有他們引用被複制到隊列和 從結構B對象,它們的值 被複制到隊列,對嗎?

這是正確的。除了值類型會被裝箱。

2.對於一個隊列,某些項目,其是小的引用和一些 項目是它們是更大的值 (約408個字節)。這會浪費 很多內存空間,如果隊列不是 很小?

這大部分是正確的。拳擊將增加另外8個字節(4個用於同步塊,4個用於類型信息),因此對於大型結構來說是微不足道的,但對於代表較大比例的較小結構。

3.你有更好的方法來做同樣的事情嗎?

最好的辦法是將該大型結構轉換爲類。根據大小知道何時選擇結構或類沒有硬性規則,但是32個字節似乎是常見的閾值。當然,您可以根據您是否真的需要值類型語義來輕鬆證明較大的結構,但408字節可能超出該閾值。如果類型真的需要值語義,你可以使它成爲一個不可變的類。

您可以進行的另一項更改是使用通用的Queue類。值類型不是像正常的Queue那樣裝箱。但是,即使使用通用版本,您仍然會複製該大型結構。

2

C# spec

由於結構不是引用類型, 這些操作的結構類型實施不同 。當結構類型的 值轉換爲 類型對象或由結構實現的接口類型 時,會發生 裝箱操作。

因此,要回答1)隊列包含盒裝結構,而不是實際的結構值。

2)的答案不在此列,盒裝結構和引用在隊列的實際分配中具有相同的大小。

對於3),我需要更多的信息。最好在隊列中擁有相同的類型,並且可以通過類和結構以適當的方式處理多態操作。過多的case語句和typeof()調用表明你的程序比面向對象更程序化。也許這就是你想要的,但是C#針對面向對象方法進行了優化。