如果我自己实现IEnumerator
接口,那么我可以(在foreach
语句内)在albumsList
内添加或删除项目而不会产生异常。但是如果{ {1}}语句使用foreach
提供的IEnumerator
,然后尝试在albumsList
中添加/删除(在foreach中)项将导致异常:
albumsList
a)我假设抛出异常的代码(使用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;
}
}
}
提供的IEnumerator
实现A
时)位于albumsList
内?
b)如果我希望能够从集合中添加/删除项目(当A
迭代它时),我是否总是需要提供我自己的foreach
接口实现,或者可以将albumsList设置为允许添加/删除项吗?
谢谢
答案 0 :(得分:15)
最简单的方法是反转for(int i = items.Count-1; i >=0; i--)
之类的项目,或循环一次,收集要在列表中删除的所有项目,然后循环删除项目,将其从原始列表中删除。
答案 1 :(得分:13)
通常不鼓励设计允许您在枚举时修改集合的集合类,除非您打算专门设计一些线程安全的东西以便这样做(例如,从一个线程添加而从另一个线程枚举)。 / p>
原因无数。这是一个。
您的MyEnumerator
课程通过递增内部计数器来工作。其Current
属性在ArrayList
中公开给定索引处的值。这意味着枚举集合并删除“每个”项目实际上不会按预期工作(即,它不会删除列表中的每个项目。)
考虑这种可能性:
您发布的代码实际上会执行此操作:
Current
的“摇滚”。你删除“摇滚。”["roll", "rain dogs"]
,您将索引增加到1,使Current
等于“雨狗”(不是“滚动”)。接下来,你删除“雨狗”。["roll"]
,您将索引增加到2(&gt; Count
);所以你的普查员认为它已经完成了。但是,这是一个有问题的实施的其他原因。例如,使用您的代码的人可能无法理解您的枚举器如何工作( 他们 - 实现应该无关紧要),因此没有意识到调用Remove
的成本foreach
块会在每次迭代时产生IndexOf
- 即线性搜索 - 的惩罚(请参阅the MSDN documentation on ArrayList.Remove
进行验证)。
基本上,我得到的是:你不想要能够从foreach
循环中删除项目(再次,除非你设计一些线程-safe ... 也许)。
好的,那么替代方案是什么?以下几点可以帮助您入门:
Clear
(删除所有项目)或RemoveAll
等方法(删除与指定过滤条件匹配的项目) )。ArrayList
已经有一个Clear
方法,就像你在.NET中可能使用的大多数集合类一样。否则,如果内部集合已编制索引,则删除多个项目的常用方法是使用for
循环从顶部索引枚举,并在需要删除的索引上调用RemoveAt
(注意这解决了两个问题)立即:通过从顶部向后退,您可以确保访问集合中的每个项目;此外,通过使用RemoveAt
代替Remove
,您可以避免重复线性搜索的惩罚。ArrayList
等非通用集合。使用强类型的通用副本(例如List(Of Album)
)(假设您有Album
类 - 否则,List(Of String)
仍然比ArrayList
更安全。答案 2 :(得分:0)
假设我有一个集合,一个数组
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);
}
会导致运行时异常
这里需要注意的是,如果允许我们为每个元素添加 在列表中
列表将变为{1,2,3,4,5,6}
然后对于每个元素和每个新添加的我们继续添加它的coz
我们将陷入无限的操作,因为它将再次为每个元素重复
答案 3 :(得分:-1)
来自INotifyCollectionChanged的MSDN文档:
您可以枚举任何集合 实现IEnumerable 接口。但是,要设置动态 绑定,以便插入或 集合中的删除更新了 UI自动,集合必须 实现INotifyCollectionChanged 接口。这个界面暴露了 必须是CollectionChanged事件 什么是潜在的 收集变化。
WPF提供了 ObservableCollection&lt;(Of&lt;(T&gt;)&gt;) class,这是一个内置的 实施数据收集 暴露了 INotifyCollectionChanged接口。 有关示例,请参见如何:创建和 绑定到ObservableCollection。
中的各个数据对象 收藏必须满足 绑定中描述的要求 来源概述。
在实施自己的之前 收集,考虑使用 ObservableCollection&lt;(Of&lt;(T&gt;)&gt;)或 现有的收藏品之一 类,例如List&lt;(Of&lt;(T&gt;)&gt;), 收集&lt;(&lt;(T&gt;)&gt;),和 BindingList&lt;(Of&lt;(T&gt;)&gt;)等等 其他
如果您有高级方案并且 想要实现自己的收藏, 考虑使用IList,它提供了一个 非泛型的对象集合 可以通过索引单独访问 并提供最佳表现。
听起来我的问题出在Collection本身,而不是它的Enumerator。