C# 具有引用成员的结构:堆还是堆栈?

C# 具有引用成员的结构:堆还是堆栈?,c#,.net,memory-management,C#,.net,Memory Management,众所周知,struct是一个,因此在堆栈上分配(例如,在特定情况下,它被装箱到类中除外) 但是,让我们考虑一下这个代码>结构> : public struct TestStruct { public List<int> items; } internal class Program { private static void Main(string[] args) { TestStruct g; } } 我想项目是在堆上分配的,不是

众所周知,
struct
是一个,因此在堆栈上分配(例如,在特定情况下,它被装箱到类中除外)

但是,让我们考虑一下这个代码>结构> <代码>:

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

internal class Program
{
    private static void Main(string[] args)
    {
        TestStruct g;
    }
}
  • 我想
    项目
    是在
    上分配的,不是吗?
    g
    “开始” 也在
    堆上
  • g
    超出范围(即
    GC
    必须为
    项目执行其工作吗
  • 如果我们没有
    列表
    ,而是有一个类型为 实施
    IDisposable
    ,最好的做法是什么
  • PS:我知道在这种情况下可以(应该?)使用
    类而不是
    结构。我只是被这个特殊的案子弄糊涂了

    我想项目是在堆上分配的,不是吗

    对。
    项的内存将在堆上分配

    g也在堆上“运行”吗

    不,结构保持在堆栈上。它只有一个字段,其中包含对堆上的
    列表的引用

    当g超出范围(即GC是否有 为项目执行其工作)

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

    如果我们没有列表,而是有一个类型为implementing的变量呢 我想知道,最好的做法是什么

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

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

    这是错误的。它被分配为声明范围的一部分。可能有更多的情况下,它实际上在堆上而不是在堆栈上(简单地说:作为另一个对象的一部分,而不是对象本身),因此这不是一个好规则

    关于您的具体问题:

  • items
    引用的对象(新列表()
  • )进入堆;字段
    items
    struct
    的一部分,在任何地方都是(并且只保存引用-本质上是一个美化的指针)
  • 当对象的所有引用都超出范围时,GC确实会考虑该对象
  • 这取决于谁拥有对象的生命周期;如果是
    TestStruct
    实例,那么最好的选择是
    TestStruct
    实际成为实现
    IDisposable
    类,并从类的
    Dispose()
    调用
    Dispose()
  • 作为额外的想法:能够编写
    g.items=newlist()
    对我来说表明这是一个非常糟糕的
    结构选择,因为易变性和
    struct
    不能很好地结合在一起(可能会有很多意想不到的bug)。要么:

    • 使
      struct
      不可变(即
      readonly
      字段,在自定义构造函数中初始化)
    • 将此设置为
    在任何一种情况下:
    public
    字段都是一个错误的选择-它应该是一个带有
    get
    的属性(如果它是
    ,可能是一个
    ,但如果它仍然是
    结构
    ,则可能不是)

    示例:

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

    Re 3:我认为在Microsoft指南中的某个地方,包含
    IDisposable
    成员的类/结构也应该是
    IDisposable
    成员-这样你就可以在
    Dispose
    中清理一次性成员。第一个子问题也在中得到了回答。这家伙解释得很好:在我看来,这是一个非常糟糕的主意有一个实现IDisposable的结构;那是没有好处的。如果它需要将其实现为no-op以满足某些通用约束,那么可能。但是对于实际的函数代码呢?非常不可靠。@MarcGravel同意你的看法-我从来没有用过结构来实现接口(坦率地说,我很少使用结构),但如果你有带一次性的结构,那么创建类的最佳选择就是实现接口(至少现代生产工具会通知你需要处理它)。但是应该使用agree类而不是struct。这在一定程度上取决于接口是否是幂等的。对于像
    IEquatable
    IComparable
    等东西,不期望接口改变状态-但是使用
    Dispose()
    有很多。如果使用
    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;
            }
        }
    }