使用事件处理程序中的yield
我有一个方法 Foo.LongRunningMethod(),它执行一些非常复杂的处理,可能会持续很长时间。在此过程中,只要遇到特定条件,它就会触发 Foo.InterestingEvent
。我希望能够公开这些事件的枚举,并且希望能够在 LongRunningMethod
实际完成之前开始迭代。换句话说,我想要的是这样的:
public IEnumerable<InterestingObject> GetInterestingObjects()
{
foo.InterestingEvent += (obj) => { yield return obj; }
foo.LongRunningMethod();
yield break;
}
不过,这不起作用,因为你不能从匿名方法中产生返回(并且因为使用 的方法>yield
不能返回 void
,而我们的事件处理程序会这样做)。还有另一种习惯用法可以让我完成这个任务吗?或者这只是一个坏主意?
I have a method Foo.LongRunningMethod()
, which does some very complicated processing that may go on for a long time. Along the way, it fires Foo.InterestingEvent
whenever it encounters a certain condition. I'd like to be able to expose an enumeration of those events, and I'd like to be able to start iterating before LongRunningMethod
actually finishes. In other words, what I want is something like this:
public IEnumerable<InterestingObject> GetInterestingObjects()
{
foo.InterestingEvent += (obj) => { yield return obj; }
foo.LongRunningMethod();
yield break;
}
This doesn't work, though, for the sensible reason that you can't yield return
from an anonymous method (and because a method using yield
cannot return void
, which our event handler does). Is there another idiom that allows me to accomplish this? Or is this just a bad idea?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您希望能够订阅来自
LongRunningMethod
的事件流,并在事件发生时从IEnumerable
生成另一个值?您可能会发现 .NET 反应式扩展很有用:http://msdn.microsoft。 com/en-us/devlabs/ee794896.aspx反应式扩展为您提供了
IObservable
,它实际上是一个仅推送的IEnumerable
。您可以围绕事件(例如您的InterestingEvent)创建一个IObservable包装器,并从那里对其进行可枚举样式的处理(例如生成对象流)。You want to be able to subscribe to a stream of events that come from
LongRunningMethod
and, when an event occurs, yield another value from anIEnumerable
? You might find the .NET Reactive Extensions useful: http://msdn.microsoft.com/en-us/devlabs/ee794896.aspxThe reactive extensions give you
IObservable
, which is in effect a push-onlyIEnumerable
. You can create anIObservable
wrapper around an event (such as yourInterestingEvent
) and do enumerable-style processing on it from there (such as yielding a stream of objects).我会在与主线程通信的单独线程中运行
LongRunningMethod()
:事件处理程序将InterestingObject
推送到某个同步队列中,并向主线程发出信号,当新的价值到来。主线程等待队列中的对象并使用
yield
返回它们。或者,您也可以在每次引发事件时阻塞子线程,直到主线程返回值并且
IEnumerable
的使用者请求下一个值。I would run
LongRunningMethod()
in a separate thread communicating with the main thread: The event handler pushes theInterestingObject
s into some synchronized queue and signals the main thread, when a new value arrives.The main thread waits for objects in the queue and returns them using
yield
to return them.Alternatively you could also block the subthread every time an event is raised until the main thread has returned the value and the consumer of the
IEnumerable
requests the next value.蒂姆·罗宾逊的回答中的这句话让我思考:
将推序列转换为拉序列很困难,这也是我的问题的根源。但相反(将拉序列变成推序列)是微不足道的,这种见解给了我解决方案。我将
LongRunningMethod
更改为内部可枚举版本,并进行了简单的重构,将每个事件回调替换为yield return
并在末尾添加yield break
。然后,我将现有的 LongRunningMethod 转换为一个包装器,它只为返回的所有内容触发事件:这保留了公共接口,同时为我提供了一个简洁的枚举,我可以将其用于需要它的场景。作为一个重要的附带好处,如果我愿意的话,这也允许我尽早放弃长时间的计算,这对于基于事件或多线程的版本来说是很难做到的。
This quote from Tim Robinson's answer got me thinking:
Turning a push sequence into a pull sequence is difficult, and the root of my problems here. But the reverse (turning a pull sequence into a push sequence) is trivial, and this insight gave me the solution. I changed
LongRunningMethod
into an internal enumerable version, with the trivial refactoring of replacing every event callback withyield return
and adding ayield break
at the end. Then I turned the existingLongRunningMethod
into a wrapper that just fires the event for everything returned:This preserves the public interface, while giving me a neat enumeration that I can use for the scenarios that require it. As a significant side-benefit, this also allows me to abandon the long computation early if I want to, something that would be difficult to do with event-based or multi-threaded versions.