如何使用响应式扩展对与事件流中特定模式匹配的内容做出反应



假设您想在文本框中的字符流中的某个单词匹配特定模式时做出反应,可以使用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中老式的事件机制来完成,而且它也没有考虑到一些文本可以在文本框的任何地方复制,或者在已经写好的单词中间按enter键等。

我的问题是,Rx是否适合这种问题,或者Rx中是否有一些东西可以应用于这里(我确实意识到还有其他方法可以解决这个问题,而不是使用Rx,我只是想了解Rx的工作原理)?

我相信这可以使用响应式扩展来完成,但是我相信你从错误的角度来处理这个问题。

您正在将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中的字符。

当然,这有点计算密集,但另一种选择是捕获任何按键可能具有的所有不同状态,并且您可能无法获得所有正确的状态。

这样,您每次都可以获得框中的单词,并且可以正确地与前面的单词进行比较。

至于这样做是否有任何价值,而不是仅仅使用原始事件,我会说是的。使用响应式扩展的两个好处是:

  • 事件流处理的逻辑封装。
  • 处理同一事件流的多个订阅。

在这个特殊的例子中,您可以从两者中获益。

您可以通过与TextChanged事件挂钩来完成单词集解析,但是您必须在声明事件处理程序(在本例中为StringBuilder)的相同结构中存储某种类型的状态。有了响应式扩展,这就不需要了(它包含在附件中)。

另外,如果您想在事件流的处理中附加一些额外的行为,则必须在一个事件处理程序或多个事件处理程序中进行两次调用。使用Reactive扩展,由于对状态的更好封装,在您选择的任何时间(取决于您订阅的时间)为同一事件的事件流添加多个处理程序更容易。

对于Rx来说这是一个很好的问题,正如其他人所演示的那样。Rx提供的好处之一是事件处理逻辑与事件处理逻辑的组合和分离。

对于事件处理程序,这是…困难。但是对于Rx,你可以有一个IObservable<string> words可以是

  • 被用于测试的替代实现取代
  • 由额外的过滤器和投影组成,独立于单词
  • 的来源
  • 被订阅,就像一个正常的事件

如果我正确理解了你的问题,那么无论何时文本框的值发生变化,你都想从文本框中生成单词流。

在Rx中可以使用:

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

然后你可以订阅上面的可观察对象来对来自文本框的单词做出反应。

你可以在正常的事件处理中这样做,但是当你需要组合多个事件流并做复杂的处理(如过滤器,缓冲区等)时,Rx的功能就发挥作用了。

相关内容

  • 没有找到相关文章

最新更新