2014-05-13 100 views
3

衆所周知,structvalue type,因此被分配到堆棧上(除了特殊情況下,例如它在類中被裝箱)。結構與參考成員:堆或堆棧?

但是讓我們考慮這個struct

public struct TestStruct 
{ 
    public List<int> items; 
} 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     TestStruct g; 
    } 
} 

我們TestStructg不是類的成員,但在Main函數宣佈「獨立」的變量。它符合stack分配變量的要求。

不過,如果我寫:

g.items = new List<int>(); 
  1. 我想items是在heap分配的,不是嗎? g「去」 heap以及?
  2. itemsg超出範圍時( GC必須爲items完成工作)會發生什麼?
  3. 如果不是List<int>,我們有一個 類型的變量實現IDisposable,那麼最好的做法是什麼?

PS:我知道在這種情況下可以(應該)使用class而不是struct。我只是被這個具體案例搞糊塗了。

+0

回覆3:我認爲這是含有類/結構'IDisposable'成員也應該是'IDisposable'自己微軟指引的地方 - 這樣你可以清理在'Dispose'中放置一次性成員。 –

+0

第一個子問題在[C#中的struct中的引用類型](http://stackoverflow.com/q/22769497/395760)中也有答案。 – delnan

+0

這個人解釋得很好:http://www.youtube.com/playlist?list=PLRwVmtr-pp07XP8UBiUJ0cyORVCmCgkdA –

回答

4

我想物品是分配在堆上的不是嗎?

是的。內存items將被分配在堆上。

g也「走」在堆上嗎?

不,結構停留在棧上。它只有字段,其中包含參考堆上的items列表。

當g超出範圍時(即GC是否有 來完成項目工作),物品會發生什麼?

如果g超出範圍,那麼在應用程序根目錄中將不會引用items。物品將變成垃圾,並在下次垃圾收集時由GC收集。在此之前,items將保留在內存中(當您退出使用它的方法時,將刪除結構實例)。

如果不是List,我們有一個變量類型實現 IDisposable,那麼最好的行爲是什麼?

最好的行動是通過你的結構實現IDisposable。更新:實際上,正如@MarcGravell指出的那樣 - 如果可能的話,最好不要在這種情況下使用結構體。

+2

IMO這是一個非常糟糕的主意,有一個實現'IDisposable'的結構;這是沒有好處的。如果它需要實現它作爲無操作來滿足一些通用約束,那麼也許。但對於實際的功能代碼?非常不可靠。 –

+0

@MarcGravell同意你 - 我從來沒有實現接口的結構(坦率地說,我很少使用結構),但是如果你有一次性* struct *,那麼創建類的最佳選擇是實現接口(至少現代生產力工具會通知你需要處理它)。但同意*類*應該用來代替struct。 –

+0

,這取決於接口是否是冪等的。對於諸如'IEquatable ','IComparable'等之類的東西,接口沒有期望改變狀態 - 但是使用Dispose()非常有用。如果使用'struct',這會使*非常容易混淆,因爲您需要考慮「我正在編輯哪個結構的副本?還有其他副本,是否知道它們被丟棄?」等等 –

3

因此被分配到堆棧上(除了特殊情況下,例如它是在類中裝箱的情況下)。

錯誤。它被分配作爲聲明範圍的一部分。可能有更多的場景,它實際上在堆上而不是在堆棧上(簡單地說:作爲另一個對象的一部分 - 而不是對象本身),所以這不是一個好的規則。

爲了您的具體問題:

  1. 對象items指(在new List<int>())那張堆;在itemsstruct,在那裏,曾經是(和剛剛成立的參考 - 基本上是一個榮耀的指針)的一部分
  2. 對象的確將通過GC時所有對它的引用超出被認爲是範圍
  3. 取決於誰擁有對象的生命週期;如果是TestStruct實例,那麼你最好的選擇然後將是TestStruct實際上是一個class實現IDisposable,並調用從類的Dispose()

隨着更多的想法Dispose():能夠編寫g.items = new List<int>();到我指出這是struct的一個非常差的選擇,因爲可變性和struct不能很好地一起玩(可能有許多意外的錯誤)。或者:

  • 使struct不變(即readonly場,在自定義構造函數初始化)
  • 使這個class

在兩種情況下:一個public場是一個不錯的選擇 - 它應該真的是一個財產get(和可能set,如果它是一個class - 但可能不會如果它仍然struct

個例子:

public struct TestStruct { 
    private readonly List<int> items; 
    public List<int> Items { get { return items; } } 
    public TestStruct(List<int> items) { 
     this.items = items; 
    } 
} 

或:

public sealed class TestClass : IDisposable { 
    private SomeDisposable items = new SomeDisposable(); 
    public SomeDisposable Items { get { return items; } } 
    public void Dispose() { 
     if(items != null) { 
      items.Dispose(); 
      items = null; 
     } 
    } 
}