【问题标题】:Avoid Collection has been modified error避免集合已被修改错误
【发布时间】:2011-09-12 09:51:11
【问题描述】:

问题:

我有以下代码:

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

显然我得到了一个错误。

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

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

想法:也许看看GetEnumerator 何时被调用?也许用IDisposable 做点聪明的事?

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

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

【问题讨论】:

  • 另一个想法:将您要保留的项目复制到另一个通用列表
  • @Jodrell,不幸的是,这对我来说不是一个选项,因为它应该在 Xbox 上运行,我正在尝试删除任何不必要的复制。
  • 为什么ItemA 有对GenericListInstanceB 的引用?
  • @George Duckett:那么你将不得不使用 for 循环,并在删除项目时修改索引。
  • @George - 您将复制引用而不是对象。我认为你不愿意这样做是被误导了。

标签: c# collections ienumerable enumeration


【解决方案1】:

您提到了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() 并忘记释放枚举器,这可能会变得很糟糕。

【讨论】:

  • 这看起来像我所追求的。一旦控制离开forloop块(无论是break还是正常退出?,Dispose方法肯定会被调用吗?
  • @George, foreach 将保证 Dispose 方法被调用。 foreach 基本上是 using (var e = o.GetEnumerator()) { while (e.MoveNext()) { ... } } 的语法糖。
  • 但即使使用此解决方案,挂起的删除也需要存储在某处。这可能是某种会消耗内存并产生 GC 压力的集合。在我看来,这不符合“我想避免任何不必要的复制,因为我想消除/最小化垃圾收集,所以我不能只迭代副本/制作新副本或类似的东西."
  • 尽管如此,如果没有这个要求,这个解决方案将是一个非常优雅的问题解决方案!
  • @Florain。确实如此,但是如果 itemstoremove / itemstoadd 列表可以是持久的(我认为可以防止嵌套的 foreach,但这没关系),那么就不应该有任何 GC 压力。
【解决方案2】:

这个怎么样。我假设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));
}

危险、老式和“臭”,但是,它可以做你想做的事,而无需专门的列表或一些魔法来捕获列表的处置。

【讨论】:

  • 感谢您的所有努力,但我认为仍有一些案例(也许我应该更清楚)。诸如添加一项并删除一项,或者添加两项等。
  • 是的,这显然行不通。在那种情况下,我唯一的想法是一个迭代函数,它可以确定下一步要去哪里。如果没有一些额外的状态,这个问题就无法解决。这是否是专用列表的内部,或者可能是用于检测操作之间更改的哈希表。是否也必须处理添加的内容?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-11
  • 1970-01-01
  • 1970-01-01
  • 2015-04-22
  • 2017-07-07
  • 2010-11-12
  • 1970-01-01
相关资源
最近更新 更多