Дебютировать пока в Rx.Net - PullRequest
1 голос
/ 07 ноября 2019

У меня есть 2 потока событий: 1. Поток событий перетаскивания мышью (перетащить начало ... перетащить конец ... перетащить начало ... перетащить конец) 2. Поток событий нажатия клавиши ('a'. .. 'b' .... 'c' .... 'd')

Мне нужно объединиться в поток, который содержит только события из вторых потоков (поэтому нажимаются только клавиши), но это необходимочтобы отфильтровать все нажатия клавиш, которые происходят между началом перетаскивания и концом перетаскивания, за исключением последнего .

Так что если источники похожи на это:

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

и

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

Результат должен быть таким:

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

Возможно ли что-то подобное с использованием Rx.net в C #?

1 Ответ

4 голосов
/ 08 ноября 2019

Ответ - да. Сначала ответьте, затем объясните:

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<T>, представляющий ваши значения, и IObservable<bool>, представляющий, где перетаскивание начинается и останавливается (истинное значение означает drag-start, и ложное значение drag-end), вы бы назвали это так:

var throttledStream= valueStream.GatedDebounce(gateStream);

Пояснение :

Чтобы лучше это понять, давайте выбросим Publish вызовы иразбить его на части:

Piece 1,

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

Эта Window функция означает, что вызов означает, что мы запускаем поднабор наблюдаемый (или окно) всякий раз, когда стробирование выдает истину, и завершаемэто окно всякий раз, когда гейтинг выдает false. В этом окне мы выбираем последний элемент, если он существует. Он будет выдан только при закрытии окна.

Piece 2,

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

Эта функция Window делает противоположное: запускает окно всякий раз, когда gating генерирует false, и завершает его всякий раз, когда gatingиспускает истину. Из этого окна мы испускаем все, когда оно появляется.

Соедините эти два с Merge, и вы получите 90% пути к вашему решению. Остальное:

  • * * * * * * * * * * * * * .StartWith(false) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1033* 1036 * - это дешевый способ убедиться, что у наших ворот есть t, f, t, f и никогда два одинаковых значения в строке, что может привести к открытию двух одновременных окон.
  • Publishзвонки - это хорошая практика для предотвращения нескольких подписок. Вы можете найти лучшие объяснения этому в некоторых других вопросах и ответах здесь.
...