繁体   English   中英

当foreach迭代时,无法在集合中添加/删除项目

[英]Can't add/remove items from a collection while foreach is iterating over it

如果我自己实现IEnumerator接口,那么我能够(在foreach语句中)添加或删除albumsList中的项而不生成异常。但是如果foreach语句使用了albumsList提供的IEnumerator ,那么尝试添加/删除(内部)来自albumsList的foreach项将导致异常:

class Program
{
    static void Main(string[] args)
    {

        string[] rockAlbums = { "rock", "roll", "rain dogs" };
        ArrayList albumsList = new ArrayList(rockAlbums);
        AlbumsCollection ac = new AlbumsCollection(albumsList);
        foreach (string item in ac)
        {
            Console.WriteLine(item);
            albumsList.Remove(item);  //works

        }

        foreach (string item in albumsList)
        {
            albumsList.Remove(item); //exception
        }



    }

    class MyEnumerator : IEnumerator
    {
        ArrayList table;
        int _current = -1;

        public Object Current
        {
            get
            {
                return table[_current];
            }
        }

        public bool MoveNext()
        {
            if (_current + 1 < table.Count)
            {
                _current++;
                return true;
            }
            else
                return false;
        }

        public void Reset()
        {
            _current = -1;
        }

        public MyEnumerator(ArrayList albums)
        {
            this.table = albums;
        }

    }

    class AlbumsCollection : IEnumerable
    {
        public ArrayList albums;

        public IEnumerator GetEnumerator()
        {
            return new MyEnumerator(this.albums);
        }

        public AlbumsCollection(ArrayList albums)
        {
            this.albums = albums;
        }
    }

}

a)第一使用时假定抛出异常(代码IEnumerator实施A通过供给albumsList )位于内部A

b)如果我想能够从集合中添加/删除项目(当foreach迭代它时),我是否总是需要提供我自己的IEnumerator接口实现,或者可以将albumsList设置为允许添加/删除项目?

谢谢

最简单的方法是反转像for(int i = items.Count-1; i >=0; i--) ,或者循环一次,收集要在列表中删除的所有项目,然后遍历项目删除,从原始列表中删除它们。

通常不鼓励设计允许您在枚举时修改集合的集合类,除非您的目的是专门设计一些线程安全的,以便这是可能的(例如,从一个线程添加而从另一个线程枚举)。

原因无数。 这是一个。

您的MyEnumerator类通过递增内部计数器来工作。 Current属性公开ArrayList给定索引处的值。 这意味着枚举集合并删除“每个”项目实际上将无法按预期工作(即,它不会删除列表中的每个项目)。

考虑这种可能性:

您发布的代码实际上会执行此操作:

  1. 首先将索引递增为0,这将为您提供“摇滚”的Current值。 你删除“摇滚”。
  2. 现在集合有["roll", "rain dogs"]并且你将索引增加到1, 使Current等于“rain dogs”(不是“roll”) 接下来,你删除“雨狗”。
  3. 现在集合有["roll"] ,你将索引增加到2(>> Count ); 所以你的普查员认为它已经完成了。

不过,还有其他原因,这是一个有问题的实现。 例如,使用你的代码的人可能无法理解你的枚举器是如何工作的(他们也不应该 - 实现应该无关紧要),因此没有意识到在foreach块中调用Remove的成本会导致IndexOf的惩罚 - 即,线性搜索 - 每次迭代(参见关于ArrayList.Remove的MSDN文档。转移以验证这一点)。

基本上,我得到的是:你不希望能够从foreach循环中删除项目(再次,除非你设计一些线程安全的东西...... 也许 )。

好的,那么替代方案是什么? 以下几点可以帮助您入门:

  1. 不要将您的集合设计为允许 - 更不用说期望 - 在枚举中进行修改。 它导致了奇怪的行为,例如我上面提供的例子。
  2. 相反,如果要提供批量删除功能,请考虑Clear (删除所有项目)或RemoveAll (删除与指定过滤器匹配的项目)等方法。
  3. 这些批量移除方法可以相当容易地实现。 ArrayList已经有一个Clear方法,就像你在.NET中可能使用的大多数集合类一样。 否则,如果您的内部集合已编制索引,则删除多个项目的常用方法是使用for循环从顶部索引枚举,并在需要删除的RemoveAt上调用RemoveAt (注意这会立即解决两个问题:从顶部,您确保访问集合中的每个项目;此外,通过使用RemoveAt而不是Remove ,您可以避免重复线性搜索的惩罚)。
  4. 作为补充说明,我强烈建议首先清除非通用集合,例如ArrayList 改为使用强类型的通用对应物(例如List(Of Album) (假设你有一个Album类 - 否则, List(Of String)仍然比ArrayList更安全)。

假设我有一个集合,一个数组

int[] a = { 1, 2, 3, 4, 5 };

我有一个功能

   public IList<int> myiterator()
        {
            List<int> lst = new List<int>();
            for (int i = 0; i <= 4; i++)
            {
                lst.Add(a[i]);
            }

              return lst;
        }

现在我调用此函数并迭代并尝试添加

   var a = myiterator1();
    foreach (var a1 in a)
       {
         a.Add(29);
       }

将导致运行时异常

这里需要注意的是,如果允许我们为列表中的每个元素添加

list将变成类似{1,2,3,4,5,6}然后对于每个元素和每个新添加的内容我们继续添加coz,我们将被卡在无限操作中,因为它将再次针对每个元素重复

INotifyCollectionChanged的MSDN文档:

您可以枚举实现IEnumerable接口的任何集合。 但是,要设置动态绑定以便集合中的插入或删除自动更新UI,集合必须实现INotifyCollectionChanged接口。 此接口公开必须在基础集合更改时引发的CollectionChanged事件。

WPF提供了ObservableCollection <(Of <(T>)>)类,它是一个公开INotifyCollectionChanged接口的数据集合的内置实现。 有关示例,请参见如何:创建和绑定到ObservableCollection。

集合中的各个数据对象必须满足绑定源概述中描述的要求。

在实现自己的集合之前,请考虑使用ObservableCollection <(Of <(T>)>)或其中一个现有集合类,例如List <(Of <(T>)>),Collection <(Of <(T>) >)和BindingList <(Of <(T>)>)等等。

如果您有高级方案并且想要实现自己的集合,请考虑使用IList,它提供可以通过索引单独访问的非泛型对象集合,并提供最佳性能。

听我说问题出在Collection本身,而不是它的Enumerator。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM