避免收集已修改错误

时间:2011-06-10 10:43:11

标签: c# collections ienumerable enumeration

问题:

我有以下代码:

foreach(var ItemA in GenericListInstanceB)
{
    ItemA.MethodThatCouldRemoveAnyItemInGenericListInstanceB();
}

显然我收到了错误。

我想要做的是将GenericListInstanceB更改为我创建的类,它会跟踪应删除的项目。然后,当循环结束时,它将全部删除它们。与添加项目相同。

这很好,我可以创建一个具有内部列表的类,以及要添加的项目列表和要删除的项目。然后另一个名为AddRemovePendingItems的方法实际上“更新”了列表(相应地删除和添加项目)。棘手的部分是让该方法自动调用。

想法:也许在查看GetEnumerator何时被调用?也许用IDisposable做一些聪明的事情?

问题:我怎么知道for循环何时退出并且我们已经完成了对我的集合类的迭代,所以我可以调用我的AddRemovePendingItems方法?,特别是如果循环中断的早期?

注意我想避免任何不必要的复制,因为我想消除/减少垃圾收集,所以我不能只是迭代复制/制作新副本或类似的东西。

2 个答案:

答案 0 :(得分:3)

您提到了IDisposable,它提供了一种可以实现此目的的方法:

public class GenericList<T> : IList<T>
{
    private class CleanupEnumerator<T> : IEnumerator<T>
    {
        private readonly GenericList<T> source;

        public CleanupEnumerator<T>(GenericList<T> source)
        {
            this.source = source;
        }

        public void Dispose()
        {
            source.RemovePendingDeletes();
        }

        /* Other IEnumerator methods here */
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new CleanupEnumerator(this);
    }

    /* Other IList methods here */
}

这将保证在使用foreach枚举您的集合时,RemovePendingDeletes()函数将在枚举器被释放时被调用。但是,请注意,如果您直接致电GetEnumerator()并忘记处置调查员,这可能会变得丑陋。

答案 1 :(得分:0)

这个怎么样?我假设MethodThatCouldRemoveAnyItemInGenericListInstanceB函数从列表中的任何位置删除一个或没有项目,不会添加任何项目。

bool finished = false;
int i = 0;
while (!finished)
{
    var itemA = genericListInstanceB[i];
    itemA.MethodThatCouldRemoveAnyItemInGenericListInstanceB();
    if (genericListInstanceB[i] == itemA)
        i++;
        // All other outcomes result in us leaving i alone
    finished = (i > (genericListInstanceB.Count - 1));
}

危险,老式和“臭”但是,它可以做你想要的,而不需要专门的清单或一些魔法来控制清单的处理。