如何打开嵌套的 IObservable aka IObservable<IObservable<T>>

How to unwrap nested IObservable aka IObservable<IObservable<T>>

我正在尝试从以下代码创建一个 IObservable<string>,但我似乎无法找到一种方法来正确解包事件处理程序的值。

发生的事情是 PasswordBox 可能会更改,所以无论何时我都想观察它,并在引发密码更改事件时提供字符串资源。如果我用普通事件来做,它工作得很好,但我很好奇使用 System.Reactive.

这将如何工作
        var passwordChanged = WhenPropertyChanged
            .Where(name => nameof(PasswordBox) == name)
            .Select(d => PasswordBox)
            .Where(d => d != null)
            .Select(box =>
            {
                return Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
                    handler => box.PasswordChanged += handler,
                    handler => box.PasswordChanged -= handler);
            }).Select(d => nameof(Password));

对我来说,似乎 Select(box => ... 部分我可以 return 一个不同的对象(除了 IObservable<IObservable<RoutedEventArgs>> ), 可用于正确订阅

像下面的作品那样做。但我认为如果端到端使用反应式,它可以更好地避免事件处理程序内存泄漏。

        var passwordHasChanged = new Subject<string>();
        // listen for changes of the password
        WhenPropertyChanged
            .Where(name => nameof(PasswordBox) == name)
            .Select(d => PasswordBox)
            .Where(d => d != null)
            .Subscribe(box =>
            {
                box.PasswordChanged += (sender, args) => passwordHasChanged.OnNext(nameof(Password));
            });

        passwordHasChanged.Subscribe(d => Log.Debug("Password changed"));

尽可能避免使用主题。 Subjects 就像 Rx 的可变变量,它们不是命令式的而不是声明式的组合和阅读。

如果您只需要最后一次输入密码的事件,请使用 Switch

SwitchIObservable<IObservable<T>> 上工作,并在获得更新的可观察对象时取消订阅先前的可观察对象。

   var passwordChanged = WhenPropertyChanged
            .Where(name => nameof(PasswordBox) == name)
            .Select(d => PasswordBox)
            .Where(d => d != null)
            .Select(box =>                
                Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
                    handler => box.PasswordChanged += handler,
                    handler => box.PasswordChanged -= handler);
            )
            .Switch()
            .Select(d => nameof(Password));