System.Collections.IEnumerator对象的意外行为

时间:2012-09-07 12:42:10

标签: c# ienumerable

我想要删除集合中的所有元素,因为我没有看到.RemoveAll或者之后我使用了下面的代码。

int number = addressBook.Items.Count;
System.Collections.IEnumerator enumerator = ...
while (enumerator.MoveNext())
{
  target = enumerator.Current as Outlook.ContactItem;
  target.Delete();
}

但是,我注意到集合中剩余元素的数量大约是每次运行程序的一半。我的结论是.Delete()单独跳到下一个元素,这意味着.MoveNext()循环的条件会在之后跳转到元素。

所以我尝试按如下方式重置枚举器。

int number = addressBook.Items.Count;
System.Collections.IEnumerator enumerator = ...
while (enumerator.MoveNext())
{
  target = enumerator.Current as Outlook.ContactItem;
  target.Delete();
  enumerator.Reset();
}

但是,当我检查.Count时,我看到在删除最后一个元素并重置枚举数之后剩余的元素数仍为1。当然,我的脸上有一个例外。

这张照片中我错过了什么?我知道这不是一个错误,因为 会在很久以前报告和解决...

4 个答案:

答案 0 :(得分:3)

只要基础集合保持不变,IEnumerator只能用于。一旦它发生变化(例如通过从中删除元素),枚举器就会无法恢复。

在修改集合时,您无法使用一个枚举器来浏览集合,而重置不会对此有所帮助。您只需每次都创建一个新的枚举器。例如。类似的东西可能有用:

while (addressBook.Items.Count > 0)
{
    foreach(Outlook.ContactItem target in ...)
    {
        target.Delete();
        break; // <-- !
    }
}

答案 1 :(得分:1)

请参阅Marc Gravell's Answer

  

重置是多余的;以至于它是一个必需品   迭代器块的语言规范在重置时抛出异常。该   正确的做法是简单地处理并释放旧的迭代器,   并再次调用GetEnumerator。或者更好:避免阅读它   两次,因为并非所有数据都是可重复的。

请参阅以下链接:

Can't add/remove items from a collection while foreach is iterating over it
When IEnumerator.Reset() method is called?

using IEnumerator to removing items

希望这有帮助..

答案 2 :(得分:1)

两件事。

  1. 只要在删除每个项目后重新实例化,您就应该能够删除枚举器中的所有项目。使用LINQ,它应该如下所示:

    while (addressBook.Items.Count > 0)
    {
        addressBook.Items
            .OfType<Outlook.ContactItem>()
            .Last()
            .Delete();
    }
    
  2. 根据MSDN,您实际上必须删除从最后一个开始的项目:http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook._contactitem.delete.aspx

答案 3 :(得分:1)

我希望使用Enumerable.Reverse扩展程序可以改进while...Last().Delete()解决方案。这可能是这样的:

foreach(var target in addressBook.Items
    .OfType<Outlook.ContactItem>()
    .Reverse())
{
    target.Delete();
}

我经常不使用Reverse;事实上,我不记得曾经在生产代码中使用它,但我也没有必要与Outlook互操作。这样做的好处是,在每个项目删除后,您不会像while...Last().Delete()方法那样重复遍历整个列表余数。当项目列表很小时,可能没有太多(任何?)性能上的差异,但是如果遍历是昂贵的(并且我想这是因为这可能归结为COM iterop在引擎盖下调用)或者如果列表是大,那么性能差异可能很大。唯一可以确定的方法是测试/配置文件。