枚举器问题,有什么方法可以避免两个循环?
我有一个第三方 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
常见的模式是做这样的事情:
Common pattern is to do something like this:
循环遍历一次并创建第二个数组,其中包含不应删除的项目。
Loop through it once and create a second array which contains the items which should not be deleted.
如果您知道它是一个集合,则可以使用 reverted for:
否则,您将不得不执行两个循环。
If you know it's a collection, you can go with reverted for:
Otherwise, you'll have to do two loops.
你可以创建这样的东西:
You can create something like this:
只要这是一个单独的项目就不是问题。规则是修改集合后不能继续进行迭代。因此:
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:
无法从枚举器中删除项目。您可以做的是复制或过滤(或两者)整个枚举序列的内容。
您可以通过使用 linq 来实现此目的并执行以下操作:
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:
您能详细说明一下您正在使用的 API 和 API 调用吗?
如果您收到
IEnumerator
或IEnumerable
,则无法从枚举器后面的序列中删除任何项目,因为没有方法可以执行此操作。当然,您不应该依赖于向下转换接收到的对象,因为实现可能会发生变化。 (实际上,设计良好的 API 根本不应该公开保存内部状态的可变对象。)如果您收到
IList
或类似的内容,您可以只使用普通的for
循环从后到前并根据需要删除项目,因为没有迭代器的状态可能会被破坏。 (这里应该再次应用有关公开可变状态的规则 - 修改返回的集合不应更改任何状态。)Can you elaborate on the API and the API calls you are using?
If you receive an
IEnumerator<T>
orIEnumerable<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 normalfor
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.)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.
为什么不类似..
Why not something like ..
一种干净、可读的方法如下(我猜测这里是第三方容器的 API,因为您没有指定它。)
对
.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.)
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 :-).
枚举器总是有一个指向真实集合的私有字段。
你可以通过反射获取它。修改它。
玩得开心。
an enumerator always has a private field pointing to the real collection.
you can get it via reflection.modify it.
have fun.