枚举器问题,有什么方法可以避免两个循环?

时间:2010-06-18 11:04:58

标签: c# loops enumerator

我有第三方api,它有一个类,可以为类中的不同项返回枚举器。

我需要删除该枚举器中的项目,因此我不能使用“for each”。我能想到的唯一选择是通过遍历枚举来获取计数,然后运行正常的for循环来删除项目。

任何人都知道避免这两个循环的方法吗?

由于

[更新] 对于这种混淆感到抱歉,但下面评论中的安德烈是对的。

这是我头脑中的一些伪代码无法正常工作,我正在寻找一个不会涉及两个循环的解决方案,但我想这是不可能的:

for each (myProperty in MyProperty)
{
if (checking some criteria here)
   MyProperty.Remove(myProperty)
}

MyProperty是实现枚举器和remove方法的第三方类。

11 个答案:

答案 0 :(得分:6)

常见模式是做这样的事情:

List<Item> forDeletion = new List<Item>();

foreach (Item i in somelist)
   if (condition for deletion) forDeletion.Add(i);

foreach (Item i in forDeletion)
   somelist.Remove(i); //or how do you delete items 

答案 1 :(得分:3)

循环遍历它并创建第二个数组,其中包含不应删除的项目。

答案 2 :(得分:2)

如果您知道它是一个集合,您可以恢复为:

for (int i = items.Count - 1; i >= 0; i--)
{
   items.RemoveAt(i);
}

否则,你将不得不做两个循环。

答案 3 :(得分:2)

您可以创建以下内容:

      public IEnumerable<item> GetMyList()
    {
        foreach (var x in thirdParty )
        {
            if (x == ignore)
                continue;
            yield return x;
        }

    }

答案 4 :(得分:2)

  

我需要删除该枚举器中的项目

只要这是一个不是问题的单个项目。规则是您在修改集合后无法继续进行迭代。因此:

foreach (var item in collection) {
    if (item.Equals(toRemove) {
        collection.Remove(toRemove);
        break;      // <== stop iterating!!
    }
}

答案 5 :(得分:1)

无法从枚举器中删除项目。您可以做的是复制或过滤(或两者)整个枚举序列的内容。 你可以通过使用linq实现这一点并像这样做:

   YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);

答案 6 :(得分:1)

您能详细说明您正在使用的API和API调用吗?

如果您收到IEnumerator<T>IEnumerable<T>,则无法从枚举器后面的序列中删除任何项目,因为没有方法可以执行此操作。当然,您应该不依赖于向下转换接收到的对象,因为实现可能会发生变化。 (实际上,设计良好的API不应该暴露出容纳内部状态的可变对象。)

如果您收到IList<T>或类似内容,您可以从后到前使用正常的for循环并根据需要删除项目,因为没有迭代器哪个状态可能已损坏。 (这里关于暴露可变状态的规则应该再次适用 - 修改返回的集合不应该改变任何状态。)

答案 7 :(得分:0)

IEnumerator.Count()将在运行时决定它需要做什么 - 枚举计数或反映它是一个集合并以这种方式调用.Count。

我喜欢SJoerd的建议,但我担心我们可能会谈论多少项目。

答案 8 :(得分:0)

为什么不能像..

 // you don't want 2 and 3
 IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
 IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });

答案 9 :(得分:0)

干净,可读的方法如下(我在这里猜测第三方容器的API,因为你还没有指定它。)

foreach(var delItem in ThirdPartyContainer.Items
                       .Where(item=>ShouldIDeleteThis(item))
                       //or: .Where(ShouldIDeleteThis)
                       .ToArray()) {
    ThirdPartyContainer.Remove(delItem);
}

.ToArray()的调用可确保在foreach迭代开始之前,所有要删除的项目都已被贪婪地缓存。

在幕后这需要一个数组和一个额外的迭代,但这通常非常便宜,而且这个方法相对于这个问题的其他答案的优点是它适用于普通的可枚举,并且不涉及棘手的可变状态难以阅读且容易出错的问题。

相比之下,反向迭代,而不是火箭科学,更容易出现一个错误,更难以阅读;并且它还依赖于集合的内部,例如不改变删除之间的顺序(例如,最好不要是二进制堆,比方说)。手动将应删除的项目添加到临时列表中只是不必要的代码 - 这就是.ToArray()会做的很好: - )。

答案 10 :(得分:0)

枚举器总是有一个指向真实集合的私有字段 你可以通过reflection.modify得到它。
玩得开心。