代码之家  ›  专栏  ›  技术社区  ›  JSBÕ±Õ¸Õ£Õ¹

使用事件处理程序的yield

  •  2
  • JSBÕ±Õ¸Õ£Õ¹  · 技术社区  · 14 年前

    我有办法 Foo.LongRunningMethod() ,它会进行一些非常复杂的处理,可能会持续很长时间。一路上,它开火了 Foo.InterestingEvent 每当它遇到某种情况。我希望能够公开这些事件的枚举,并且能够在之前开始迭代 LongRunningMethod

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

    不过,这不起作用,因为你不能这样做 yield return 来自一个匿名方法(因为 yield 无法返回 void

    3 回复  |  直到 14 年前
        1
  •  5
  •   Community CDub    4 年前

    您希望能够订阅来自 LongRunningMethod 并且,当事件发生时,从 IEnumerable ? 您可能会发现.NET响应式扩展非常有用: http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx

    IObservable ,这实际上只是一个推力 . 您可以创建 可观察的 包装事件(例如 InterestingEvent )并在此基础上对其执行可枚举样式的处理(例如生成一个对象流)。

    编辑: I可数 ).

    拉和推可能不会协调,因此需要在某个地方缓冲在拉之前推的新值。最直接的方法可能是采用生产者-消费者的安排:把它们推到一个新的市场 List<T> GetInterestingObjects

    根据事件的引发方式,您可能需要将生产者和使用者放在不同的线程上(所有这些都是被动扩展最终要做的,当您要求它在 I可数 .)

        2
  •  2
  •   MartinStettner    14 年前

    我会跑的 LongRunningMethod() 在与主线程通信的单独线程中:事件处理程序将 InterestingObject 当一个新的值到达时,它会进入某个同步队列并向主线程发出信号。

    主线程等待队列中的对象并使用 yield 归还他们。

    IEnumerable

        3
  •  1
  •   JSBÕ±Õ¸Õ£Õ¹    14 年前

    Tim Robinson的回答让我想到:

    您要做的是将一个push序列(事件的出现)转换为pull序列(调用为IEnumerable)。

    把一个推序列转换成一个拉序列是困难的,我的问题的根源就在这里。但反过来(将拉序列转换为推序列)是微不足道的,这一见解为我提供了解决方案。我变了 LongRunningMethod 到一个内部可枚举的版本中,通过简单的重构将每个事件回调替换为 yield return yield break 最后。然后我把现有的 到一个包装器中,该包装器只为返回的所有内容触发事件:

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

    这保留了公共接口,同时为我提供了一个整洁的枚举,可以用于需要它的场景。作为一个重要的副作用,如果我愿意的话,这也允许我尽早放弃长时间的计算,这对于基于事件或多线程的版本来说是很难做到的。