【问题标题】:struct with reference members : heap or stack?带有引用成员的结构:堆还是堆栈?
【发布时间】:2014-05-13 10:02:16
【问题描述】:

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

但是让我们考虑一下struct

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

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

我们的TestStruct g 不是类的成员,而是在Main 函数中声明的“独立”变量。它符合stack-allocated 变量的要求。

但是,如果我写:

g.items = new List<int>();
  1. 我想items 是分配在heap 上的,不是吗? g“去”了吗 heap 也一样?
  2. g 超出范围时,items 会发生什么(即 GC 必须为items 做它的工作?
  3. 如果不是List&lt;int&gt; 我们有一个类型的变量怎么办 实施IDisposable,最好的做法是什么?

PS:我知道在这种情况下可以(应该?)使用class 而不是struct。我只是对这个具体案例感到困惑。

【问题讨论】:

标签: c# .net memory-management


【解决方案1】:

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

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

g 是否也在堆上“去”?

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

当 g 超出范围时,项目会发生什么(即 GC 是否有 为物品做它的工作)?

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

如果我们有一个实现类型的变量而不是 List 怎么办 IDisposable,最好的做法是什么?

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

【讨论】:

  • IMO 拥有一个实现 IDisposable 的结构是一个非常糟糕的主意;没有什么好处。如果它需要将其实现为无操作以满足某些通用约束,那么也许可以。但是对于实际的功能代码呢?非常不可靠。
  • @MarcGravell 同意你的观点——我从来没有实现接口的结构(坦率地说我很少使用结构),但如果你有一次性的 struct 那么创建类的最佳选择是实现接口(至少现代生产力工具会通知您需要处理它)。但同意应该使用 class 代替 struct。
  • 这有点取决于接口是否是幂等的。对于像IEquatable&lt;T&gt;IComparable 等这样的事情,不希望接口改变状态 - 但对于Dispose(),情况非常多。如果使用struct,这可能会非常感到困惑,因为您需要考虑“我正在编辑结构的哪个副本?是否还有其他副本,知道他们被处置了?”等
【解决方案2】:

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

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

对于您的具体问题:

  1. items 引用的对象new List&lt;int&gt;())进入堆; 字段 itemsstruct 的一部分,无论它在哪里(并且只保存引用 - 本质上是一个美化的指针)
  2. 所有对它的引用超出范围时,GC确实会考虑对象
  3. 这取决于谁拥有对象的生命周期;如果它是TestStruct 实例,那么您最好的选择那么TestStruct 实际上是实现IDisposableclass,并从类的@987654330 调用Dispose() @

另外的想法:能够给我写g.items = new List&lt;int&gt;(); 表明这是struct 的一个非常糟糕的选择,因为可变性和struct 不能很好地结合在一起(可能会有很多意想不到的错误)。要么:

  • 使struct 不可变(即readonly 字段,在自定义构造函数中初始化)
  • 将此设为class

在任何一种情况下:public 字段都是一个糟糕的选择 - 它实际上应该是一个带有 get 的属性(如果它是 class 则可能是 set - 但如果它仍然存在则可能不是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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2019-10-25
    • 2018-01-06
    • 2013-10-29
    • 2012-07-28
    • 1970-01-01
    • 2021-12-12
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多