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
,最好的做法是什么类而不是结构。我只是被这个特殊的案子弄糊涂了
我想项目是在堆上分配的,不是吗
对。项的内存将在堆上分配
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;
}
}
}