3

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

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

有人知道避免这两个循环的方法吗?

谢谢

[更新]很抱歉造成混乱,但下面评论中的安德烈是对的。

这是我脑海中的一些伪代码,它不起作用,我正在寻找一个不涉及两个循环的解决方案,但我想这是不可能的:

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

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

4

11 回答 11

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 
于 2010-06-18T11:08:30.953 回答
3

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

于 2010-06-18T11:06:57.773 回答
2

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

只要这是一个单一的项目,这不是问题。规则是修改集合后不能继续迭代。因此:

foreach (var item in collection) {
    if (item.Equals(toRemove) {
        collection.Remove(toRemove);
        break;      // <== stop iterating!!
    }
}
于 2010-06-18T12:42:22.613 回答
2

如果您知道这是一个集合,则可以使用 reverted :

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

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

于 2010-06-18T11:11:10.597 回答
2

您可以创建如下内容:

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

    }
于 2010-06-18T11:14:00.753 回答
1

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

   YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
于 2010-06-18T11:11:25.157 回答
1

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

如果您收到一个IEnumerator<T>IEnumerable<T>您无法从枚举数后面的序列中删除任何项目,因为没有方法可以这样做。你当然不应该依赖向下转换收到的对象,因为实现可能会改变。(实际上,一个设计良好的 API 根本不应该暴露持有内部状态的可变对象。)

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

于 2010-06-18T11:12:16.420 回答
0

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

于 2010-06-19T06:53:25.603 回答
0

为什么不像..

 // you don't want 2 and 3
 IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
 IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
于 2010-06-18T11:58:09.270 回答
0

一种干净、易读的方法如下(我在这里猜测第三方容器的 API,因为您没有指定它。)

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

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

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

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

于 2010-06-18T12:41:24.877 回答
0

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

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

于 2010-06-18T11:23:34.280 回答