[英]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
给定索引处的值。 这意味着枚举集合并删除“每个”项目实际上将无法按预期工作(即,它不会删除列表中的每个项目)。
考虑这种可能性:
您发布的代码实际上会执行此操作:
Current
值。 你删除“摇滚”。 ["roll", "rain dogs"]
并且你将索引增加到1, 使Current
等于“rain dogs”(不是“roll”) 。 接下来,你删除“雨狗”。 ["roll"]
,你将索引增加到2(>> Count
); 所以你的普查员认为它已经完成了。 不过,还有其他原因,这是一个有问题的实现。 例如,使用你的代码的人可能无法理解你的枚举器是如何工作的(他们也不应该 - 实现应该无关紧要),因此没有意识到在foreach
块中调用Remove
的成本会导致IndexOf
的惩罚 - 即,线性搜索 - 每次迭代(参见关于ArrayList.Remove
的MSDN文档。转移以验证这一点)。
基本上,我得到的是:你不希望能够从foreach
循环中删除项目(再次,除非你设计一些线程安全的东西...... 也许 )。
好的,那么替代方案是什么? 以下几点可以帮助您入门:
Clear
(删除所有项目)或RemoveAll
(删除与指定过滤器匹配的项目)等方法。 ArrayList
已经有一个Clear
方法,就像你在.NET中可能使用的大多数集合类一样。 否则,如果您的内部集合已编制索引,则删除多个项目的常用方法是使用for
循环从顶部索引枚举,并在需要删除的RemoveAt
上调用RemoveAt
(注意这会立即解决两个问题:从顶部,您确保访问集合中的每个项目;此外,通过使用RemoveAt
而不是Remove
,您可以避免重复线性搜索的惩罚)。 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.