C# .NET ReactiveExtensions:将Sample()与变量timespan一起使用
给定一个高频可观察的数据流,我只想每XX秒发射一个项目 这通常在RX中通过使用.Sample(TimeSpan.FromSeconds(XX))完成 然而。。。我希望时间间隔根据数据的某些属性而变化 假设我的数据是: 阶级地位 { ... 公共交通速度; } 如果速度小于100,我希望每5秒发出一次数据。如果速度高于100,则应每2秒一次C# .NET ReactiveExtensions:将Sample()与变量timespan一起使用,c#,system.reactive,rx.net,C#,System.reactive,Rx.net,给定一个高频可观察的数据流,我只想每XX秒发射一个项目 这通常在RX中通过使用.Sample(TimeSpan.FromSeconds(XX))完成 然而。。。我希望时间间隔根据数据的某些属性而变化 假设我的数据是: 阶级地位 { ... 公共交通速度; } 如果速度小于100,我希望每5秒发出一次数据。如果速度高于100,则应每2秒一次 使用现成的示例()可以这样做吗?或者我需要自己构建一些东西吗?让我知道这是否有效: var query = source .Publi
使用现成的示例()可以这样做吗?或者我需要自己构建一些东西吗?让我知道这是否有效:
var query =
source
.Publish(ss =>
ss
.Select(s => s.Speed < 100 ? 5.0 : 2.0)
.Distinct()
.Select(x => ss.Sample(TimeSpan.FromSeconds(x))));
var查询=
来源
.Publish(ss=>
党卫军
.选择(s=>s.速度<100?5.0:2.0)
.Distinct()
.选择(x=>ss.Sample(TimeSpan.FromSeconds(x));
这里是一个低级实现,使用System.Reactive.Concurrency.Scheduler.SchedulePeriodic
扩展方法作为计时器
public static IObservable<TSource> Sample<TSource>(this IObservable<TSource> source,
Func<TSource, TimeSpan> intervalSelector, IScheduler scheduler = null)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (intervalSelector == null)
throw new ArgumentNullException(nameof(intervalSelector));
scheduler = scheduler ?? Scheduler.Default;
return Observable.Create<TSource>(observer =>
{
TimeSpan currentInterval = Timeout.InfiniteTimeSpan;
IDisposable timer = null;
TSource latestItem = default;
bool latestEmitted = true;
object locker = new object();
Action periodicAction = () =>
{
TSource itemToEmit;
lock (locker)
{
if (latestEmitted) return;
itemToEmit = latestItem;
latestItem = default;
latestEmitted = true;
}
observer.OnNext(itemToEmit);
};
return source.Subscribe(onNext: item =>
{
lock (locker)
{
latestItem = item;
latestEmitted = false;
}
var newInterval = intervalSelector(item);
if (newInterval != currentInterval)
{
timer?.Dispose();
timer = scheduler.SchedulePeriodic(newInterval, periodicAction);
currentInterval = newInterval;
}
}, onError: ex =>
{
timer?.Dispose();
observer.OnError(ex);
}, onCompleted: () =>
{
timer?.Dispose();
observer.OnCompleted();
});
});
}
公共静态IObservable示例(此IObservable源,
Func intervalSelector,isScheduler scheduler=null)
{
如果(source==null)抛出新的ArgumentNullException(nameof(source));
if(intervalSelector==null)
抛出新ArgumentNullException(nameof(intervalSelector));
调度器=调度器??调度器。默认值;
返回可观察的。创建(观察者=>
{
TimeSpan currentInterval=Timeout.InfiniteTimeSpan;
IDisposable timer=null;
TSource latestItem=默认值;
bool latestEmitted=真;
对象锁定器=新对象();
动作周期动作=()=>
{
TSource itemToEmit;
锁(储物柜)
{
如果(延迟提交)返回;
itemToEmit=最晚的事件;
latestItem=默认值;
latestEmitted=真;
}
OnNext观察员(itemToEmit);
};
返回源。订阅(onNext:item=>
{
锁(储物柜)
{
latestItem=项目;
延迟提交=错误;
}
var newInterval=间隔选择符(项目);
如果(新间隔!=当前间隔)
{
计时器?.Dispose();
timer=scheduler.SchedulePeriodic(newInterval,periodiAction);
currentInterval=newInterval;
}
},onError:ex=>
{
计时器?.Dispose();
观察员:OnError(ex);
},未完成:()=>
{
计时器?.Dispose();
observer.OnCompleted();
});
});
}
用法示例:
observable.Sample(x => TimeSpan.FromSeconds(x.Speed < 100 ? 5.0 : 2.0));
observable.Sample(x=>TimeSpan.FromSeconds(x.Speed<100?5.0:2.0));
每当intervalSelector
回调返回不同的间隔时,计时器就会重新启动。在极端情况下,间隔随每个新项而改变,那么此自定义运算符的行为将更像内置运算符,而不是内置运算符
与示例
不同,油门
的周期是一个滑动窗口。每次油门
接收到一个值时,窗口都会重置。()
是的,这是可能的。@CarstenGehling,我的荣幸!