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

发布于 2024-12-01 20:09:07 字数 827 浏览 0 评论 0原文

假设您想在文本框中的字符流中的某个单词与特定模式匹配时做出反应,可以使用 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)?

Suppose you want to react when some word in a stream of characters in a text box matches a certain pattern, could this be done using Rx? So if the initial source looks like this:

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

To just create an observable word stream from this I imagine you could subscribe to charStream and post OnNext when ever you see a word separator:

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();
    }
});

The observer of wordObservable could then filter out the words matching the pattern. However, the above could easily be accomplished with the old-fashioned event mechanism in .NET and it also doesn't take in to account that some text can be copied in at any place in the text box or that enter is pressed in a middle of an already written word etc.

My question is, is Rx a bad fit for this kind of problem or are there some things in Rx that could be applied here (I do realize that there are other ways of solving this than by using Rx, I'm only trying to understand the workings of Rx here)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我为君王 2024-12-08 20:09:07

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

您正在查看 KeyPress event作为获取字符流的方式。这是不正确的,KeyPress 事件的可观察结果就是按键。是的,它们有时对应于单词中的字符,但正如您提到的,有些按键与单词中的字符无关,而是其他操作。

也就是说,您不应该订阅 KeyPress 事件,而应该订阅 TextChanged 事件。然后,您的 IObservable应该返回与单词相对应的字符串实例数组。

换句话说,不是流式传输按键并尝试以附加方式找出框中的单词,而是获取框中的整个文本并更改框中的单词您观察到的事物:

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< /a>) 声明于。反应性扩展,这本来是不需要的(它包含在外壳中)。

此外,如果您想要将一些附加行为附加到事件流的处理中,则必须在一个事件处理程序或多个事件处理程序中进行两次调用。使用响应式扩展,由于状态封装得更好,可以更轻松地将事件流的多个处理程序添加到同一事件,无论您选择什么时间(取决于您订阅的时间)。

I believe that it could be done using the Reactive Extensions, but I believe you're approaching the problem from the wrong perspective.

You are viewing the KeyPress event as the way to obtain a stream of characters. That's incorrect, the observable of the KeyPress events is just that, key presses. Yes, they correspond to characters in words at times, but as you mentioned, there are key presses that have nothing to do with characters in words, but other operations.

That said, you shouldn't be subscribing to the KeyPress event, but rather, the TextChanged event. Then, your IObservable<T> should return an array of string instances which correspond to the words.

In other words, instead of streaming the key presses and trying to figure out what words are in the box in an additive way, take the entire text in the box and make the change of the words in the box the thing you observe on:

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;
});

From here, you subscribe to the changes in the words as a set, not just characters in the TextBox.

Granted, it's a little more computationally intensive, but the other alternative is to capture all the different states that any key press would have, and you might not get all of them correct.

This way, you'll get the words in the box every time and can compare against previous words correctly.

As to whether or not there is any value doing this as opposed to just using raw events, I would say yes. Two of the great benefits of using the Reactive extensions are:

  • Encapsulation of logic for processing of event streams.
  • Handles multiple subscriptions to the same event stream.

In this particular example, you benefit from both.

You could have done the word set parsing by hooking to the TextChanged event, but you would have then had to have stored some sort of state in the same structure that the event handler (in this case, the StringBuilder) is declared in. With the Reactive extensions, that would have not been required (it's contained in the enclosure).

Additionally, if you wanted to attach some additional behavior to the processing of a stream of events, you would have to have two calls in one event handler, or multiple event handlers. With the Reactive extensions, because of the better encapsulation of state, it's easier to add multiple handlers for the stream of events to the same event, at whatever time you choose (depending on when you subscribe).

诺曦 2024-12-08 20:09:07

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

对于事件处理程序,这......很困难。但使用 Rx,您可以拥有一个 IObservableWords 可以

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

This is a fine problem for Rx, as others have demonstrated how. One of the benefits that Rx provides is composition and separation of event processing logic from event handling logic.

With event handlers, this is... difficult. But with Rx, you can have an IObservable<string> words which can be

  • Replaced by an alternate implementation for testing
  • Composed with additional filters and projections, independent of the source of the words
  • Be subscribed to, just like a normal event
贩梦商人 2024-12-08 20:09:07

如果我正确理解了您的问题,那么每当文本框值更改时,您都希望从文本框生成单词流。

这可以在 Rx 中轻松完成,使用:

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

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

您可以在正常的事件处理中执行此操作,但当您需要组合多个事件流并执行过滤器、缓冲区等复杂处理时,Rx 的力量就会发挥作用。

If I have understood your question properly then you want to generate stream of words from the text box whenever the text box value is changed.

This can be done easily in Rx using:

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

Then you can subscribe to the above observable to react to words coming from Text box.

You can do this in normal event handling but the power of Rx comes into play when you need to compose multiple event streams and do complex processing like filter, buffer etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文