我希望通过IObservable<T>
查找与谓词匹配的元素,如果找不到,则返回IObservable<T>
的最后一个元素。我不想存储IObservable<T>
的全部内容,而且我不想遍历IObservable
两次,所以我设置了一个扩展方法
public static class ObservableExtensions
{
public static IObservable<T> FirstOrLastAsync<T>(this IObservable<T> source, Func<T, bool> pred)
{
return Observable.Create<T>(o =>
{
var hot = source.Publish();
var store = new AsyncSubject<T>();
var d1 = hot.Subscribe(store);
var d2 = hot.FirstAsync(x => pred(x)).Amb(store).Subscribe(o);
var d3 = hot.Connect();
return new CompositeDisposable(d1, d2, d3);
});
}
public static T FirstOrLast<T>(this IObservable<T> source, Func<T, bool> pred)
{
return source.FirstOrLastAsync(pred).Wait();
}
}
Async方法从传入的可能冷的方法创建一个热的observable。它订阅AsyncSubject<T>
以记住最后一个元素,以及IObservable<T>
查找该元素。然后它从IObservable<T>
中的任何一个获取第一个元素,它首先通过.Amb
返回一个值(AsyncSubject<T>
在获得.OnCompleted
之前不会返回值消息)。
我的问题如下:
var d2 = hot.Where(x => pred(x)).Take(1).Amb(store).Subscribe(o);
我对RX很新,这是我对IObservable的第一次扩展。
修改的
我最终选择了
public static class ObservableExtensions
{
public static IObservable<T> FirstOrLastAsync<T>(this IObservable<T> source, Func<T, bool> pred)
{
var hot = source.Publish().RefCount();
return hot.TakeLast(1).Amb(hot.Where(pred).Take(1).Concat(Observable.Never<T>()));
}
public static T FirstOrLast<T>(this IObservable<T> source, Func<T, bool> pred)
{
return source.FirstOrLastAsync(pred).First();
}
}
答案 0 :(得分:1)
你可以将你想要的两个案例放在一起。
如果您的源观察结果很冷,则可以执行Publish|Refcount
。
public static IObservable<T> FirstOrLast<T>(this IObservable<T> source, Func<T, bool> predicate)
{
return source.TakeLast(1).Amb(source.Where(predicate).Take(1));
}
测试:
var source = Observable.Interval(TimeSpan.FromSeconds(0.1))
.Take(10)
.Publish()
.RefCount();
FirstOrLast(source, i => i == 5).Subscribe(Console.WriteLine); //5
FirstOrLast(source, i => i == 11).Subscribe(Console.WriteLine); //9
答案 1 :(得分:0)
我试图制作一个“更简单”的查询,但到目前为止还没有。
如果我坚持你的基本结构,我可以提供一点点改进。试试这个:
public static IObservable<T> FirstOrLastAsync<T>(
this IObservable<T> source, Func<T, bool> pred)
{
return Observable.Create<T>(o =>
{
var hot = source.Publish();
var store = new AsyncSubject<T>();
var d1 = hot.Subscribe(store);
var d2 =
hot
.Where(x => pred(x))
.Concat(store)
.Take(1)
.Subscribe(o);
var d3 = hot.Connect();
return new CompositeDisposable(d1, d2, d3);
});
}
这并不是更好,但我比使用Amb
更喜欢它。我认为这只是一个清洁工。