反应式扩展中的可观察模式

6

我正在尝试理解下面的代码,试图使用响应式扩展库。

 IObservable<string> textChangedObservable =
            Observable.FromEventPattern<TextChangedEventArgs>(searchScrip, "TextChanged")
                .Select(evt => ((TextBox) sender).Text);

 textChangedObservable.Subscribe(OnNext, OnCompleted);

private void OnNext(string s)
    {
        System.Diagnostics.Debug.Print("OnNext " + s + "\n");
    }

    private void OnCompleted()
    {
        System.Diagnostics.Debug.Print("OnCompleted " + "\n");
    }

如果我在输入框中输入SEARC,输出结果如下:
  • OnNext SE
  • OnNext SEA
  • OnNext SEA
  • OnNext SEAR
  • OnNext SEAR
  • OnNext SEAR
  • OnNext SEARC
  • OnNext SEARC
  • OnNext SEARC
  • OnNext SEARC

    1. "S"为什么没有触发OnNext?
    2. 为什么OnCompleted从未被调用?
    3. 为什么第n个字符的OnNext调用n-1次?

这不是一个容易的问题,因为类 XamTextEditor 不容易获取。我怀疑这可能与它实现的 RoutedPropertyChanged 有关。 - supertopi
实际上,您需要告诉我们XAML元素searchScripsender是什么。 - supertopi
将代码示例更改为使用 WPF 文本框以保持简单。但行为与问题中的仍然相同。 - asb
3个回答

3

看起来你正在订阅你的 searchScrip_TextChanged 处理程序中的 observable。

这意味着在第一次调用 searchScrip_TextChanged 之前,S 已经发生了,而你还没有连接 observable。所以它当然不会触发。

但现在 S 已经被触发,你有一个订阅,所以当输入 E 时,你会得到一个单独的 SE。但由于 searchScrip_TextChanged 处理程序也会被调用,因此现在你有了两个订阅。

所以当输入 A 时,你会得到两个 SEA,因为你有两个 observable。但同样地,searchScrip_TextChanged 也会被调用,所以现在你有了三个 observables。

等等,等等。

事件不会自动完成。你需要手动处理订阅以结束它们。这很有道理,因为这是你想要停止的普通事件处理程序所必须做的。

你应该在窗体加载时创建你的 observable,这样它只会被创建一次。

应该像这样:

IObservable<string> textChangedObservable =
        Observable.FromEventPattern<TextChangedEventArgs>(searchScrip, "TextChanged")
            .Select(evt => searchScrip.Text);

IDisposable subscription =
    textChangedObservable
        .Subscribe(
            s => Debug.Print("OnNext " + s + "\n"),
            s => Debug.Print("OnCompleted\n"));

2
这里的问题实际上与 Rx 无关。
1: 为什么 "S" 没有触发 OnNext?
因为你订阅的 TextChanged 事件没有在第一个 S 上触发。
2: 为什么 OnCompleted 从未被调用?
当你将 .NET 事件作为 IObservable 封装时,你永远不会收到 OnError 或 OnCompleted 通知。在 .NET 事件中不存在错误或完成的概念。
如果有两个事件,一个用于值,另一个用于完成,你可以像这样将它们组合起来:
var values = Observable.FromEvent(...);
var completion = Observable.FromEvent(...);
var query = values.TakeUntil(completion);

现在query将产生一个正确的OnCompleted通知。

3: 为什么第n个字符时OnNext会被调用n-1次?

因为你订阅了TextChanged事件,它就是这样触发的。正如@Kari-Antti所指出的那样,这可能是使用“路由属性”事件的副作用。


我的搜索脚本的XAML为<igWPF:XamTextEditor Name="searchScrip" TextChanged="searchScrip_TextChanged" />,当我设置断点时,在实现“private void searchScrip_TextChanged(object sender, RoutedPropertyChangedEventArgs<string> e)”中,“S”确实被触发。 - asb
@asb,除了我已经说过的,我不知道如何帮助你。可观察对象将与底层事件没有任何区别地发出。这是一个非常简单的机制。因此,你所说的可观察对象和事件不起作用的情况,与我所知道的相矛盾。 - Timothy Shields
@TimothyShields - 他重复订阅了可观察对象 - 这就是为什么 OnNext 被调用了 n - 1 次的原因。 - Enigmativity
@TimothyShields - 当键入“S”时,可观察对象尚未被订阅。 - Enigmativity
@Enigmativity 我忽略了它们出现了0,1,2,...次的事实。 - Timothy Shields

-1

可能是因为您使用了RoutedPropertyChangedEventArgs?

如果您使用PropertyChangedEventHandler呢?

Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
                h => yourModel.propertyChanged += h,
                h => yourModel.propertyChanged -= h)
            .Where(x => x.EventArgs.PropertyName = "your_property_name");
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接