2010-07-15 26 views
2

我有一个方法Foo.LongRunningMethod(),它执行一些可能会持续很长时间的非常复杂的处理。一路上,只要遇到一定的情况,它就会触发Foo.InterestingEvent。我希望能够展示这些事件的枚举,并且我希望能够在LongRunningMethod实际完成之前开始迭代。换句话说,我想是这样的:使用来自事件处理程序的良率

public IEnumerable<InterestingObject> GetInterestingObjects() 
{ 
    foo.InterestingEvent += (obj) => { yield return obj; } 
    foo.LongRunningMethod(); 

    yield break; 
} 

这不起作用,但是,对于合理的理由,你不能从yield return匿名方法(因为使用yield不能返回的方法void,我们的事件处理程序)。还有另外一个成语可以让我完成这个吗?或者这只是一个坏主意?

回答

1

本报价由蒂姆·罗宾逊的回答让我思考:

你在做什么是转推序列(事件的发生)为拉一个(调用到IEnumerable的)。

将一个按压序列转换为一个拉序列是很困难的,也是我的问题的根源。但是相反(将拉序列转换为推序列)是微不足道的,这种见解给了我解决方案。我将LongRunningMethod更改为一个内部可枚举版本,其中包含用yield return替换每个事件回调并在末尾添加yield break的普通重构。然后我把现有LongRunningMethod成只是触发事件的一切包装返回:

internal IEnumerable<InterestingObject> FindInterestingObjects() 
{ 
    /* etc */ 
} 

public void LongRunningMethod() 
{ 
    foreach (var obj in FindInterestingObjects()) 
    { 
     OnInterestingEvent(obj); 
    } 
} 

这会保留公共接口,同时给我一个整洁的枚举,我可以使用,为需要的方案。作为一个重要的副作用,这也使我能够在需要时尽早放弃长计算,这对于基于事件或多线程的版本来说很难做到。

+0

他何时何地说这些?我能读什么来了解更多关于推拉的信息? – zsf222 2016-08-29 11:08:48

5

您希望能够订阅来自LongRunningMethod的事件流,并且在事件发生时从IEnumerable产生另一个值?您可能会发现.NET Reactive Extensions非常有用:http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx

被动扩展为您提供IObservable,实际上这只是推式IEnumerable。您可以创建一个包含事件(例如InterestingEvent)的IObservable包装,并从那里对其执行枚举式处理(例如生成对象流)。

编辑:“有没有,让我来完成这个成语”不是采用微软新库等?你正在做的是将推动序列(事件发生)转换为拉动序列(调用IEnumerable)。

拉和推可能不会被协调,所以你需要在某个地方缓存推拉之前推送的新值。最直接的方法可能是采用生产者 - 消费者安排:将其推入GetInterestingObjects调用者使用的List<T>

根据事件发生的方式,您可能需要将生产者和使用者放在不同的线程上。 (所有这一切有什么反应的扩展做什么,最终当你问到一个IObservableIEnumerable之间进行转换。)

2

我会在一个单独的线程与主线程通信运行LongRunningMethod():事件处理程序将InterestingObject推入某个同步队列,并在新值到达时通知主线程。

主线程等待队列中的对象并使用yield返回它们以返回它们。

或者,您也可以在每次引发事件时阻塞子线程,直到主线程返回值并且IEnumerable的使用者请求下一个值。

相关问题