【问题标题】:Debounce Until in Rx.Net在 Rx.Net 中去抖直到
【发布时间】:2019-11-07 16:41:34
【问题描述】:

我有 2 个事件流: 1.鼠标拖放事件流(拖动开始...拖动结束...拖动开始...拖动结束) 2. 按键事件流('a' ... 'b' .... 'c' .... 'd')

我需要组合成一个只包含来自第二个流的事件的流(所以只有按键),但它需要过滤掉在拖动开始和拖动结束之间发生的所有按键,除了最后一个一个

所以如果来源是这样的:

... Start ............... End .............. Start .............. End

...........'a'...'b'...'c'.......'d'...'e'..........'f'....'g'.......

结果应该是这样的:

...........................'c'...'d'...'e'..........................'g'

在 C# 中使用 Rx.net 可以实现这样的事情吗?

【问题讨论】:

  • 我内心的贪婪只想回答“是”而不提供如何。 :)
  • 到目前为止,你已经提供了一个答案,而且也是一个很好的答案:-)

标签: reactive-programming system.reactive rx.net


【解决方案1】:

答案是肯定的。先回答再解释:

public static class X 
{

    public static IObservable<T> GatedDebounce<T>(this IObservable<T> source, IObservable<bool> gating)
    {
        var finalStream = gating
            .StartWith(false)
            .DistinctUntilChanged()
            .Publish(_gating => source.Publish(_source => Observable.Merge(
                _source
                    .Window(_gating.Where(b => b), _ => _gating.Where(b => !b))
                    .SelectMany(o => o.LastAsync()),
                _source
                    .Window(_gating.Where(b => !b), _ => _gating.Where(b => b))
                    .Merge()
            )));
        return finalStream;
    }

}

然后,给定一个代表您的值的 IObservable&lt;T&gt; 和一个代表拖动开始和停止位置的 IObservable&lt;bool&gt;(true 表示拖动开始,false 表示拖动结束),您可以这样称呼它:

var throttledStream= valueStream.GatedDebounce(gateStream);

解释

为了更好地理解它,让我们扔掉Publish 调用,并将其分解为多个部分:

第 1 部分,

source
    .Window(gating.Where(b => b), _ => gating.Where(b => !b))
    .SelectMany(o => o.LastAsync())

这个Window 函数意味着调用意味着我们在门控发出真时启动一个子集可观察(或窗口),并在门控发出假时结束该窗口。从该窗口中,我们选择最后一项(如果存在)。这只会在窗口关闭时发出。

第 2 部分,

source
    .Window(gating.Where(b => !b), _ => gating.Where(b => b))
    .Merge() //Equivalent to .SelectMany(o => o) if you prefer

这个Window 函数的作用正好相反:只要 gating 发出 false 就启动一个窗口,并且只要 gating 发出 true 就结束它。当它到达时,我们从那个窗口发出所有东西。

将这两者与Merge 放在一起,您就可以完成 90% 的解决方案。其余的:

  • .StartWith(false) 是为了确保我们在您最初启动 observable 时打开一个窗口,否则在第一个门控项目之前发生的值会丢失。
  • DistintUntilChanged() 是一种廉价的方法,可以确保我们的门是 t、f、t、f 并且绝不会连续两个相同的值,这会导致同时打开两个窗口。
  • Publish 调用是防止多次订阅的好习惯。您可以在此处的其他问答中找到更好的解释。

【讨论】:

  • 与问题没有直接关系,但你能推荐一个好的和 更新 Rx.net 在线参考指南吗? RxJS 有很多很棒的参考资料,我几乎每天都在使用它们,但由于某种原因,Rx.net 的文档几乎没有。
  • 我会推荐 introtorx.com 和 reactivex.com。两者都不是最新的,但它们都提供了您所需的 95%。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多