当它们从集合中删除时会发生值类型吗?

Dan*_*Tao 4 .net c# heap stack garbage-collection

假设我有一些struct像这样的简单:

public struct WeightedInt {
    public int value;
    public double weight;
}
Run Code Online (Sandbox Code Playgroud)

那么假设我有一个这种结构的实例集合:

List<WeightedInt> weightedInts = new List<WeightedInt>();
Run Code Online (Sandbox Code Playgroud)

正如我理解值类型与引用类型一样,值类型在堆栈上分配,因此一旦实例化所述对象的函数终止,就会从内存中清除值类型对象.这意味着在以下代码中:

void AddWeightedIntToList(int value, double weight) {
    WeightedInt wint = new WeightedInt();
    wint.value = value;
    wint.weight = weight;

    weightedInts.Add(wint);
}
Run Code Online (Sandbox Code Playgroud)

添加局部变量的副本,而完成后局部变量本身将从内存中删除.wintweightedIntsAddWeightedIntToList

首先:这是正确的吗?

其次,这个wint存储副本在哪里存储?它不能在堆栈上,因为一旦函数完成它就会消失(对吗?).这是否意味着副本与weightedInts?一起存储在堆上?它被删除后是垃圾收集,好像它是一个引用类型的实例?

这个问题肯定可以在某个地方的文章中得到解答,在这种情况下,该文章的链接将是一个完全可以接受的答案.我没有找到任何运气.

Ree*_*sey 6

首先:这是正确的吗?

是.一旦范围结束,原件就"消失".

其次,这个wint副本存储在哪里?它不能在堆栈上,因为一旦函数完成它就会消失(对吗?).这是否意味着副本与weightedInts一起存储在堆上?它被删除后是垃圾收集,好像它是一个引用类型的实例?

您的实例List<WeightedInt>在堆上创建一个数组.当您将其"添加"到列表中时,您将为该数组的一部分分配值类型的副本.该值保存在堆上,作为数组的一部分(List类的内部).

当您的加权成员超出范围时,它将变为无根,并且有资格被垃圾收集.在此之后的某个时刻,GC将运行,并将释放与其内部阵列相关联的内存,从而释放与您的wint副本相关联的内存.


编辑:

另外,当你打电话:

weightedInts.Remove(wint);
Run Code Online (Sandbox Code Playgroud)

发生了一些事情(带List<T>).

首先,列表查找值类型的FIRST实例的索引,该索引等于wint.然后它调用RemoteAt(索引).

RemoveAt(index)方法基本上标记内部大小较小,然后检查您要删除的索引.如果它位于列表的中间,它实际上使用Array.Copy将所有值类型实例复制到一个元素,以"缩小"列表.然后它将数组末尾的内存清零.

数组本身不会缩小,因此删除元素不会释放内存.如果您想要回收这个内存(或者甚至让它有资格通过GC释放),您需要打电话List<T>.TrimExcess().