.net 使用Rx执行一次延迟操作,每个窗口取消

.net 使用Rx执行一次延迟操作,每个窗口取消,.net,system.reactive,.net,System.reactive,具体地说,我试图模拟执行一个动作,如果按下键的持续时间大于阈值(T) 我正在尝试使用无状态变量的reactiveextensions.NET(稳定的1.0版本)来实现这一点。 这是我的输入和我需要的大理石图: 让T=3(因此,没有上键事件的4 dddd构成“按下键”) 向下键:--dddd--dd--d-dddddddd---- keyUp:------------u-----u--u-------u-- 期望的:-------------a-------------------a-------

具体地说,我试图模拟执行一个动作,如果按下键的持续时间大于阈值(T)

我正在尝试使用无状态变量的reactiveextensions.NET(稳定的1.0版本)来实现这一点。

这是我的输入和我需要的大理石图:

让T=3(因此,没有上键事件的4 dddd构成“按下键”)

向下键:--dddd--dd--d-dddddddd----

keyUp:------------u-----u--u-------u--

期望的:-------------a-------------------a----------

下面是我提出的一些示例代码,它可以工作,但使用了一个状态变量

var keyDownStream = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown").Where(args => args.EventArgs.Key == Key.OemPeriod);
        var keyUpStream = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp").Where(args => args.EventArgs.Key == Key.OemPeriod);

        var repeatGuard = false;
        keyUpStream.Subscribe(x => repeatGuard = false);
        keyDownStream
            .DelayOrCancel(TimeSpan.FromSeconds(2.0), keyUpStream)
            .Where(_ => repeatGuard == false)
            .Do(_ =>
            {
                repeatGuard = true;
            })
            .Subscribe(
                result =>
                {
                    Console.WriteLine("KeyHold");
                }
            );

public static class JustinsRx
{
    public static IObservable<T> DelayOrCancel<T, TCancel>(this IObservable<T> source,
                                     TimeSpan delay,
                                     IObservable<TCancel> cancel)
    {
        //argument checking skipped
        return from s in source
               from i in Observable.Timer(delay).TakeUntil(cancel)
               select s;
    }
}
var-keydroaddown=Observable.FromEventPattern(这是“KeyDown”)。其中(args=>args.EventArgs.Key==Key.OemPeriod);
var keyUpStream=Observable.FromEventPattern(这是“KeyUp”)。其中(args=>args.EventArgs.Key==Key.OemPeriod);
var-repeatGuard=false;
订阅(x=>repeatGuard=false);
钥匙下游
.DelayOrCancel(TimeSpan.FromSeconds(2.0),键上游)
.Where(=>repeatGuard==false)
.Do(=>
{
repeatGuard=true;
})
.订阅(
结果=>
{
控制台。写入线(“键控”);
}
);
公共静态类
{
公共静态IObservable DelayOrCancel(此IObservable源,
时间间隔延迟,
IObservable(取消)
{
//已跳过参数检查
从源中的s返回
从i开始,在可观察计时器(延迟)中,直到(取消)
选择s;
}
}
这对我很有用:

var timer = Observable.Timer(TimeSpan.FromSeconds(1.0));

var query =
    keyDownStream
        .Select(_ =>
            keyUpStream
                .Select(_ => 'u')
                .Amb(timer.Select(_ => 't'))
                .Take(1)
                .Where(x => x == 'u')
                .Select(_ => Unit.Default))
        .Switch();

这是可行的,但我觉得可以缩短

var firstKeyDowns = Observable
    .Merge(keyDownStream.Select(_ => 'd'), keyUpStream.Select(_ => 'u'))
    .DistinctUntilChanged()
    .Where(c => c == 'd');
var query = from s in firstKeyDowns 
            from i in Observable.Timer(delay).TakeUntil(keyUpStream)
            select s;
编辑:这里有一个我认为更好的版本:

var noRepeats = Observable
    .Merge(keyDownStream.Select(_ => 'd'), keyUpStream.Select(_ => 'u'))
    .DistinctUntilChanged();
var query = noRepeats
    .Throttle(delay)
    .Where(c => c == 'd');

进度更新:我正在尝试此查询,但它实际上阻止了调用方!有人知道为什么这会阻止呼叫者,使窗口甚至不显示吗?keyDownStream.DelayOrCancel(TimeSpan.FromSeconds(2),keyUpStream).Window(()=>keyUpStream).Subscribe(result=>{if(result.First()==result.Last())Console.WriteLine(“KeyHold”);//do stuff});+1这可能是它得到的最短的时间,并且是对内置操作符的合理使用。或者,类似的东西可以在传递给DelayOrCancel之前进行输入过滤。谢谢Matthew,我会看看你和Enigmativity的解决方案,我会给出正确的答案,告诉谁的解决方案与我的大理石图/代码示例完全一样,没有副作用。如果两者都有效,那么我将以性能为基础。再次感谢!再给我几个小时,我会给你们回复的。举手!伟大的解决方案,我可以看到,由于使用了DistinctUntilChanged,它的性能甚至比我的有状态代码好得多。这甚至还不接近:)@JustinMoser-怎么会呢?它似乎符合要求。你能告诉我什么不起作用吗?对不起,我认为你没有测试它,因为它不能编译。它不起作用的原因是,OnNext在keyUp事件发生之前不会发生,这是由于“Where(x=>x=='u')”引起的。修复此查询并非易事。(您不能只删除where,因为还有其他问题,例如OnNext会为每个keyDown事件触发,而不仅仅是持续时间后的第一个问题。