【问题标题】:Using yield from an event handler从事件处理程序中使用 yield
【发布时间】:2010-07-15 21:54:52
【问题描述】:

我有一个方法Foo.LongRunningMethod(),它做了一些可能会持续很长时间的非常复杂的处理。在此过程中,它会在遇到特定条件时触发Foo.InterestingEvent。我希望能够公开这些事件的枚举,并且我希望能够在 LongRunningMethod 实际完成之前开始迭代。换句话说,我想要的是这样的:

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

    yield break;
}

但是,这不起作用,因为您不能从匿名方法中yield return(并且因为使用yield 的方法不能返回void,我们的事件处理程序会这样做)。还有另一个成语可以让我做到这一点吗?或者这只是一个坏主意?

【问题讨论】:

    标签: c# events yield


    【解决方案1】:

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

    响应式扩展为您提供IObservable,这实际上是一个仅推送的IEnumerable。您可以围绕一个事件(例如您的 InterestingEvent)创建一个 IObservable 包装器,并从那里对其进行可枚举式处理(例如产生一个对象流)。

    编辑:除了采用 Microsoft 的新库之外,“有没有一个成语可以让我做到这一点”?您正在做的是将推送序列(事件的发生)转变为拉取序列(调用IEnumerable)。

    拉取和推送可能不会协调,因此您需要在某个地方缓冲在拉取之前推送的新值。最直接的方法可能是采用生产者-消费者安排:将它们推送到 List&lt;T&gt; 中,由 GetInterestingObjects 的调用者使用。

    根据引发事件的方式,您可能需要将生产者和消费者放在不同的线程上。 (当你要求它在IObservableIEnumerable 之间转换时,所有这些都是响应式扩展最终会做的事情。)

    【讨论】:

      【解决方案2】:

      我将在与主线程通信的单独线程中运行LongRunningMethod():事件处理程序将InterestingObjects 推送到某个同步队列中,并在新值到达时向主线程发出信号。

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

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

      【讨论】:

        【解决方案3】:

        蒂姆·罗宾逊的回答引我思考:

        您正在做的是将推送序列(事件的发生)转变为拉动序列(调用 IEnumerable)。

        将推送序列转换为拉取序列很困难,这也是我的问题的根源。但是反过来(将拉序列转换为推序列)是微不足道的,这种洞察力给了我解决方案。我将LongRunningMethod 更改为内部可枚举版本,并通过简单的重构将每个事件回调替换为yield return,并在末尾添加yield break。然后我将现有的LongRunningMethod 变成了一个包装器,它只为返回的所有内容触发事件:

        internal IEnumerable<InterestingObject> FindInterestingObjects() 
        { 
            /* etc */ 
        }
        
        public void LongRunningMethod()
        {
            foreach (var obj in FindInterestingObjects())
            {
                OnInterestingEvent(obj);
            }
        }
        

        这保留了公共接口,同时给了我一个简洁的枚举,我可以在需要它的场景中使用它。作为一个重要的附带好处,如果我愿意,这还允许我尽早放弃长时间的计算,这对于基于事件或多线程的版本来说很难做到。

        【讨论】:

        • 他是在何时何地说这番话的?我可以阅读哪些内容来了解​​有关推拉的更多信息?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-25
        • 2016-05-23
        • 1970-01-01
        • 2013-07-13
        相关资源
        最近更新 更多