如何在Rx中使用新的BufferWithTimeOrCount返回IObservable <iobservable <t>&gt;而不是IObservable <ilist <t>&gt; </ilist <t> </iobservable <t>

时间:2011-01-30 07:00:37

标签: system.reactive

在Windows Phone 7上有一个新版本的 BufferWithTimeOrCount 扩展方法,用于IObservable,它返回“流的流”而不是之前的“列表流”。我在尝试使用新旧方法时遇到困难,所以也许我只是不明白它是如何工作的,但我的目标是创建一个只在现有流与指定的基于时间的模式匹配时才会触发的流。之前的2个触摸事件。到目前为止,我已经为TouchUp和TouchDown创建了流(参见related question),并且在伪代码中我想要类似的东西:

//BufferLast2 should contain the last 1 or 2 touch events that occurred in the last 500ms. If no touches occurred this should return an empty set
var BufferLast2 = TouchDown.Merge(TouchUp).BufferWithTimeOrCount(TimeSpan.FromSeconds(0.5), 2);
//Use BufferLast2 to detect tap (TouchDown then TouchUp occuring in less than 0.5s)
var TouchTap = from touch2 in BufferLast2
               where touch2.Count == 2 && touch2.First().Action == TouchAction.Down && touch2.Last().Action == TouchAction.Up
               select touch2.First(); //returns initial TouchDown event
//Use BufferLast2 to detect Hold (TouchDown with no TouchUp occuring in 0.5s)
var TouchHold = from touch2 in BufferLast2
                where touch2.Count == 1 && touch2.First().Action == TouchAction.Down
                select touch2.First(); //returns initial TouchDown event

当使用内置于ROM中的“稳定”Microsoft.Phone.Reactive版本的Rx调用IObservable<Class>.BufferWithTimeOrCount(...)时,返回IObservable<IList<Class>>,这很容易使用标准列表运算符(如上所述),但由于某种原因,BufferLast2总是返回两个down事件,而不是我期望的Down-&gt; Up序列。

我认为它可能是代码中的错误,所以我尝试添加对Rx的latest version的引用,并使用来自 C:\ Program Files(x86)\ Microsoft Cloud Programmability的Observable Extensions \ Reactive Extensions \ v1.0.2838.0 \ WP7 \ System.Reactive.dll ,其中BufferWithTimeOrCount(...)返回IObservable<IObservable<Class>>。这使得像Where x.Count == 2Where x.First().P == ...这样的简单过滤器更难编写。我实际上并没有想出如何在这个返回值上做一个像x.Count() == 2这样的简单过滤器,而不会创建一个完全独立的订阅或Subject对象,这个过程太复杂了。这可能是一个简单的错误,就像我的上一个问题(我需要的只是Where子句:-P)但它确实让我疯狂。有什么帮助吗?

2 个答案:

答案 0 :(得分:3)

更改api使缓冲看起来更像Rx-y并且适合他们的Window运算符实现(如果使用反射器,您将能够使用Window查看Buffer运算符,这不会感到惊讶)。我认为他们改变它的原因可能有很多种。我不会再猜测他们,因为他们比我聪明得多!

所以这是我的解决方案。可能有一种更清晰的方式来获得你想要的东西,但我可能会实现我自己的扩展方法来缓冲到列表中。也许是这样的:

public static class BufferToList
{
   public static IObservable<IEnumerable<TSource>> BufferToList<TSource>(this IObservable<TSource> source)
    {
       return Observable.CreateWithDisposable<IEnumerable<TSource>>(observer => 
          {
             var list = new List<TSource>();

             return source.Subscribe(list.Add,
               observer.OnError,
               () =>
               {
                  observer.OnNext(list);
                  observer.OnCompleted();
               });
          });
    }
}

然后像:

TouchDown.Merge(TouchUp)
   .BufferWithTimeOrCount(TimeSpan.FromSeconds(0.5), 2)
   .Select(bufferedValues => bufferedValues.BufferToList())
   .Subscribe(OnBufferOpen)

private void OnBufferOpen(IObservable<IEnumerable<IEvent<IEventArgs>>> bufferedListAsync)
{
   bufferedListAsync.Where(list => list.Count() == 2);
}

我建议如果你想彻底解释为什么他们改变了api,那就去问rx forums on msdn

答案 1 :(得分:3)

最新版本的Rx v1.0.2856.0提供缓冲区和窗口。对于缓冲区,我们基于IList恢复了原始签名。相应的窗口运算符将返回嵌套的可观察序列。

实现Buffer *运算符的方法是使用新的ToList扩展方法组合相应的Window *运算符,该方法将IObservable带入IObservable&gt;。所有Buffer *操作符都在SelectMany选择器中调用这个新的ToList操作符。