Как реагировать на то, что соответствует определенному шаблону из потока событий, используя Reactive Extensions? - PullRequest
1 голос
/ 29 августа 2011

Предположим, вы хотите отреагировать, когда какое-либо слово в потоке символов в текстовом поле соответствует определенному шаблону. Можно ли это сделать с помощью Rx? Так что если исходный источник выглядит так:

var charStream = Observable.
    FromEventPattern<KeyPressEventArgs>(textBox, "KeyPress");

Чтобы просто создать наблюдаемый поток слов из этого, я думаю, вы могли бы подписаться на charStream и публиковать OnNext, когда бы вы ни увидели разделитель слов:

StringBuilder word = new StringBuilder();
var res = charStream.Subscribe(keyCharEv => {
    if (!Char.IsWordSeparator(keyCharEv.EventArgs.KeyChar))
        word.Append(keyCharEv.EventArgs.KeyChar);
    else
    {
        wordObservable.OnNext(word.ToString());
        word.Clear();
    }
});

Наблюдатель wordObservable может затем отфильтровать слова, соответствующие шаблону. Тем не менее, вышесказанное можно легко выполнить с помощью старомодного механизма событий в .NET, и при этом также не учитывается, что некоторый текст может быть скопирован в любом месте текстового поля или что нажатие ввода происходит в середине. уже написанного слова и т. д.

У меня такой вопрос: Rx плохо подходит для такого рода проблем или есть какие-то вещи в Rx, которые можно применить здесь (я понимаю, что есть и другие способы решения этой проблемы, кроме использования Rx, я только пытаясь понять работу Rx здесь)?

Ответы [ 3 ]

3 голосов
/ 29 августа 2011

Я считаю, что это можно сделать с помощью Reactive Extensions , но я полагаю, что вы подходите к проблеме с неправильной точки зрения.

Вы просматриваете событие KeyPress как способ получения потока символов. Это неправильно, наблюдаемые события KeyPress - это просто нажатия клавиш. Да, они соответствуют символам в словах время от времени , но, как вы упомянули, есть нажатия клавиш, которые не имеют ничего общего с символами в словах, кроме других операций.

Тем не менее, вам следует подписаться не на событие KeyPress, а на TextChanged событие . Затем ваш IObservable<T> должен вернуть массив экземпляров строки, соответствующих словам.

Другими словами, вместо того, чтобы передавать нажатия клавиш и пытаться выяснить, какие слова находятся в блоке аддитивным способом, возьмите весь текст в поле и внесите изменения в слова в коробка, на которой вы наблюдаете:

var textChanges = Observable.
    FromEventPattern<EventHandler>(textBox, "TextChanged");

// Create an observable that contains the splits.
IObservable<IList<string>> observableWordChanges = textChanges.Select(e => { 
    // Get the text.
    string text = (e.Sender as TextBox).Text;

    // The current word.
    var word = new StringBuilder();

    // The list of words.
    IList<string> words = new List<string>();

    // Parse.
    foreach (char c in text)
    {
        if (!Char.IsWordSeparator(c))
            word.Append(c);
        else
        {
            // Add to the words.
            words.Add(word.ToString());

            // Clear the builder.
            word.Clear();
        }
    }

    // Return the words.
    return words;
});

Отсюда вы подписываетесь на изменения слов в виде набора, а не только символов в TextBox.

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

Таким образом, вы будете каждый раз получать слова в поле и сможете корректно сравнивать их с предыдущими словами.

Относительно того, есть ли какая-либо ценность, делающая это, а не просто использование необработанных событий, я бы сказал, да. Два из больших преимуществ использования расширений Reactive:

  • Инкапсуляция логики для обработки потоков событий.
  • Обрабатывает несколько подписок на один и тот же поток событий.

В этом конкретном примере вы получаете выгоду от обоих.

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

Кроме того, если вы хотите добавить какое-то дополнительное поведение к обработке потока событий, вам потребуется два вызова в одном обработчике событий или несколько обработчиков событий. С помощью расширений Reactive благодаря лучшей инкапсуляции состояния проще добавлять несколько обработчиков для потока событий к одному и тому же событию в любое время по вашему выбору (в зависимости от того, когда вы подписываетесь).

2 голосов
/ 29 августа 2011

Это хорошая проблема для Rx, как другие продемонстрировали, как.Одним из преимуществ Rx является составление и отделение логики обработки событий от логики обработки событий.

С обработчиками событий это ... сложно.Но с Rx вы можете иметь IObservable<string> words, который может быть

  • Заменен альтернативной реализацией для тестирования
  • Составлен с дополнительными фильтрами и проекциями, независимо от источника слов
  • Будьте подписаны, как обычное событие
0 голосов
/ 29 августа 2011

Если я правильно понял ваш вопрос, то вы хотите генерировать поток слов из текстового поля при каждом изменении значения текстового поля.

Это легко сделать в Rx, используя:

Observable.FromEvent<EventArgs>(txt, "TextChanged")
    .Select(e => ((TextBox)e.Sender).Text)
    .SelectMany(t => t.Split(" ".ToCharArray()).Where(
        a => !String.IsNullOrWhiteSpace(a)))

Затем вы можете подписаться на вышеприведенную заметку, чтобы реагировать на слова, поступающие из текстового поля.

Вы можете сделать это при обычной обработке событий, но мощь Rx вступает в игру, когда вам нужно создать несколько потоков событий и выполнить сложную обработку, такую ​​как фильтр, буфер и т. Д.

...