使用 IEnumerator的模式在接口中
我有一个 C# 类,需要通过一堆方法处理一系列项目 (IEnumerable
),因此我不能简单地在方法内 foreach
。我调用 .GetEnumerator()
并传递此 IEnumerator
,它非常有效,为我在循环单个序列时提供了所需的灵活性。
现在我想允许其他人在这个过程中添加逻辑。最自然的方法是为它们提供一个接口,其中包含接受 IEnumerator
的方法。简单,完成,并且有效。
但我担心这是一种反模式。他们必须知道 IEnumerator
已经调用了 .MoveNext()
,因此他们可以简单地访问 .Current
。另外,我没有看到任何在要实现的接口中使用 IEnumerator
的先例。
- 我没有考虑哪些陷阱?
- 是否有另一种模式允许我使用相同的高效机制(即我不希望创建/销毁多个副本)而不暴露
IEnumerator
本身?
更新:正如我在下面的评论中提到的:我想要的是某种通用的Stream
。我需要能够有效地查看下一个项目 (IEnumerator.Current
-> .Peek()
) 并使用它 (IEnumerator
-> .Pop()
)。
我使用了 IEnumerator
因为它符合账单界面。我更喜欢在合适的情况下使用常见的 BCL 类型,但我似乎滥用了这一类型。
那么问题3)有没有一个类可以满足这个需求?或者我应该创建自己的 Stream,它在内部延迟执行 IEnumeratorIEnumerable
iteslf。
好吧,听起来共识是 IEnumerator
通常是 ValueType
并且不知道 IEnumerator
,传递它通常是一个坏主意。
我听到的最好的建议是创建我自己的类并进行传递。还有其他建议吗?
I have a C# class which needs to process a sequence of items (IEnumerable<T>
) across a bunch of methods, so I cannot simply foreach
inside a method. I call .GetEnumerator()
and pass this IEnumerator<T>
around and it works great giving me the flexibility I need while looping through a single sequence.
Now I want to allow others to add logic into this process. The most natural way to do this is give them an interface with a method that accepts the IEnumerator<T>
. Easy, done, and it works.
But I'm concerned that this is an anti-pattern. They have to know that the IEnumerator<T>
has already had .MoveNext()
called, so they can simply access .Current
. Plus I don't see any precedent for using IEnumerator<T>
in interfaces to be implemented.
- What pitfalls am I not considering?
- Is there another pattern which will allow me this same efficient mechanism (i.e. I don't want multiple copies being created/destroyed) without exposing the
IEnumerator<T>
itself?
Update: As I mentioned in a comment below: What I want is some sort of generic Stream<T>
. I need to be able to effectively see the next item (IEnumerator.Current
-> .Peek()
) and consume it (IEnumerator<T>.MoveNext()
-> .Pop()
).
I used IEnumerator<T>
because it fit the bill interface wise. I prefer to use common BCL types when they fit, but it seemed like I was abusing this one.
So question 3) Is there a class which fits this need? Or should I just create my own Stream which lazily executes the IEnumerator<T>
internally? Then it would be entirely encapsulated. I'd like to not use many of the existing collections as they have internal storage, whereas I'd like the storage to be the IEnumerable<T>
iteslf.
OK it sounds like the consensus is that do to IEnumerator<T>
often being a ValueType
as well as not knowing a priori the state of the IEnumerator<T>
, that it is generally a bad idea to pass it around.
The best suggestion I've heard is to create my own class which gets passed around. Any other suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您绝对不应该传递
IEnumerator
。除此之外,在某些情况下它可能会产生一些非常奇怪的效果。例如,您希望这里发生什么?尝试进行一些更改:
并
尝试预测每种情况下的结果:)
现在不可否认,这是一个特别邪恶的示例,但它确实演示了与传递可变状态相关的一些极端情况。我强烈鼓励您在“中心”方法中执行所有迭代,该方法仅使用当前值调用适当的其他方法。
You should definitely not pass the
IEnumerator<T>
around. Apart from anything else, it could have some very bizarre effects in some cases. What would you expect to happen here, for example?A couple of changes to try:
and
Try to predict the results in each case :)
Now admittedly that's a particularly evil example, but it does demonstrate some corner cases associated with passing around mutable state. I would strongly encourage you to perform all your iteration in a "central" method which calls into appropriate other methods just with the current value.
我强烈建议不要传递枚举器本身;除了需要当前值之外,您还有什么理由这样做?
除非我遗漏了一些明显的东西,否则我建议您的实用程序函数只需将您枚举的类型作为参数,然后使用一个外部
foreach
循环来处理实际的枚举。也许您可以提供一些额外的信息来说明您迄今为止为何做出此设计决策。
I would strongly advise against passing the enumerator itself around; what reason do you have for this, aside from needing the current value?
Unless I'm missing something obvious, I would recommend having your utility functions simply take the type that you're enumerating as a parameter, then have a single outer
foreach
loop that handles the actual enumeration.Perhaps you can provide some additional information as to why you've made this design decision so far.
在我看来,您可能会受益于使用事件,以便您可以将要处理的项目的通知推送给侦听器。常规 .NET 事件按照订阅的顺序进行处理,因此如果需要排序,您可能会采用更明确的方法。
您可能还想查看响应式框架。
Sounds to me like you might benefit from using an event so that you can push notification of items to be processed out to listeners. Regular .NET events are handled in the order they're subscribed, so you might go for a more explicit approach if ordering is required.
You may also like to look at the Reactive Framework.
如果我理解正确的话,您有许多方法都可以在序列上调用 MoveNext,并且您希望这些方法彼此协作,因此您传递了
IEnumerator
。正如您所提到的,这里肯定存在一些紧密耦合,因为您希望枚举器在每个方法的入口处处于特定状态。听起来你真正想要的是类似 Stream 类的东西,它既是一个集合(某种程度上)又是一个迭代器(具有当前位置的概念)。我会将您的迭代和您需要的任何其他状态包装在您自己的类中,并将各种方法作为该类的成员If I understand this correctly, you have a number of methods that can all call MoveNext on the sequence and you want these methods to cooperate with each-other, so you pass around an
IEnumerator<T>
. There's definitely some tight coupling here, as you mentioned, since you expect the enumerator to be in a particular state at the entrance to each method. It sounds like what you're really after here is something like the Stream class, which is both a collection (sort of) and an iterator (has a notion of Current location). I would wrap your iteration and any other state you need in your own class and have the various methods as members of that class