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

发布于 2024-09-06 00:30:49 字数 440 浏览 12 评论 0原文

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

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

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

谢谢

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

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

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

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

I have a third party api, which has a class that returns an enumerator for different items in the class.

I need to remove an item in that enumerator, so I cannot use "for each". Only option I can think of is to get the count by iterating over the enum and then run a normal for loop to remove the items.

Anyone know of a way to avoid the two loops?

Thanks

[update] sorry for the confusion but Andrey below in comments is right.

Here is some pseudo code out of my head that won't work and for which I am looking a solution which won't involve two loops but I guess it's not possible:

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

MyProperty is the third party class that implements the enumerator and the remove method.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

装纯掩盖桑 2024-09-13 00:30:49

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

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 

Common pattern is to do something like this:

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 
夜光 2024-09-13 00:30:49

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

Loop through it once and create a second array which contains the items which should not be deleted.

硬不硬你别怂 2024-09-13 00:30:49

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

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

否则,您将不得不执行两个循环。

If you know it's a collection, you can go with reverted for:

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

Otherwise, you'll have to do two loops.

遥远的她 2024-09-13 00:30:49

你可以创建这样的东西:

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

    }

You can create something like this:

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

    }
哆啦不做梦 2024-09-13 00:30:49

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

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

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

I need to remove an item in that enumerator

As long as this is a single item that's not a problem. The rule is that you cannot continue to iterate after modifying the collection. Thus:

foreach (var item in collection) {
    if (item.Equals(toRemove) {
        collection.Remove(toRemove);
        break;      // <== stop iterating!!
    }
}
铁憨憨 2024-09-13 00:30:49

无法从枚举器中删除项目。您可以做的是复制或过滤(或两者)整个枚举序列的内容。
您可以通过使用 linq 来实现此目的并执行以下操作:

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

It is not possible to remove an item from an Enumerator. What you can do is to copy or filter(or both) the content of the whole enumeration sequence.
You can achieve this by using linq and do smth like this:

   YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
感情旳空白 2024-09-13 00:30:49

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

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

如果您收到 IList 或类似的内容,您可以只使用普通的 for 循环从后到前并根据需要删除项目,因为没有迭代器的状态可能会被破坏。 (这里应该再次应用有关公开可变状态的规则 - 修改返回的集合不应更改任何状态。)

Can you elaborate on the API and the API calls you are using?

If you receive an IEnumerator<T> or IEnumerable<T> you cannot remove any item from the sequence behind the enumerator because there is no method to do so. And you should of course not rely on down casting an received object because the implementation may change. (Actually a well designed API should not expose mutable objects holding internal state at all.)

If you receive IList<T> or something similar you can just use a normal for loop from back to front and remove the items as needed because there is no iterator which state could be corrupted. (Here the rule about exposing mutable state should apply again - modifying the returned collection should not change any state.)

浊酒尽余欢 2024-09-13 00:30:49

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

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

IEnumerator.Count() will decide at run-time what it needs to do - enumerate to count or reflect to see it's a collection and call .Count that way.

I like SJoerd's suggestion but I worry about how many items we may be talking about.

假装不在乎 2024-09-13 00:30:49

为什么不类似..

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

Why not something like ..

 // you don't want 2 and 3
 IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
 IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
压抑⊿情绪 2024-09-13 00:30:49

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

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

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

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

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

A clean, readable way to do this is as follows (I'm guessing at the third-party container's API here since you haven't specified it.)

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

The call to .ToArray() ensures that all items to be deleted have been greedily cached before the foreach iteration begins.

Behind the scenes this involves an array and an extra iteration over that, but that's generally very cheap, and the advantage of this method over the other answers to this question is that it works on plain enumerables and does not involve tricky mutable state issues that are hard to read and easy to get wrong.

By contrast, iterating in reverse, while not rocket science, is much more prone to off-by-one errors and harder to read; and it also relies on internals of the collection such as not changing order in between deletions (e.g. better not be a binary heap, say). Manually adding items that should be deleted to a temporary list is just unnecessary code - that's what .ToArray() will do just fine :-).

羞稚 2024-09-13 00:30:49

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

an enumerator always has a private field pointing to the real collection.
you can get it via reflection.modify it.
have fun.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文