带引用成员的struct:heap还是stack?

时间:2014-05-13 10:02:17

标签: c# .net memory-management

众所周知,structvalue type,因此在堆栈上分配(例如,在类中装箱的特定情况除外)。

但请考虑一下struct

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

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

我们的TestStruct g不是班级的成员,而是一个&#34;独立的&#34; Main函数中声明的变量。它符合stack - 已分配变量的要求。

但是,如果我写:

g.items = new List<int>();
  1. 我认为items已分配heap了吗?是g&#34;去&#34; 还在heap上?
  2. items超出范围时,g会发生什么? GC必须为items)工作吗?
  3. 如果不是List<int>我们有一个类型的变量怎么办? 实施IDisposable,最佳的行动方案是什么?
  4. PS:我知道在这种情况下可以(应该?)使用class而不是struct。我只是对这个具体案例感到困惑。

2 个答案:

答案 0 :(得分:6)

  

我认为在堆上分配的项目不是吗?

是。 items的内存将在堆上分配。

  

g&#34; go&#34;在堆上也是?

不,struct停留在堆栈上。它只有一个字段,它在堆上保存对items列表的引用。

  

当g超出范围时(例如GC有),项目会发生什么   做项目的工作)?

如果g超出范围,则应用程序根目录中将不会引用items。项目将变为垃圾,并将在下一次垃圾收集期间由GC收集。在此之前items将保留在内存中(当您使用它时退出方法将删除结构实例)。

  

如果不是List,我们有一个实现类型的变量   IDisisposable,什么是最好的行动方案?

最佳操作是通过结构实现IDisposable。更新:实际上正如@MarcGravell指出的那样 - 如果可能的话,最好不要在这种情况下使用struct。

答案 1 :(得分:4)

  

因此被分配在堆栈上(例如,在类中装箱的特定情况除外)。

错了。它被分配为声明范围的一部分。可能有更多的场景,它实际上在堆上而不是堆栈上(简单地说:作为另一个对象的一部分 - 而不是对象本身),所以这不是一个好的规则。

针对您的具体问题:

  1. items引用的对象new List<int>())在堆上; 字段 itemsstruct的一部分,其中任何地方都是(并且只保存引用 - 本质上是一个美化的指针)
  2. 所有对它的引用超出范围时,GC会考虑对象
  3. 取决于谁拥有对象的生命周期;如果它是TestStruct实例,那么然后最好的选择是TestStruct实际上是class实现IDisposable,并且来自班级Dispose()
  4. 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;
            }
        }
    }