【问题标题】:How do I merge several observables using WhenAny(...) in ReactiveUI?如何在 ReactiveUI 中使用 WhenAny(...) 合并多个可观察对象?
【发布时间】:2014-02-14 10:54:43
【问题描述】:

我有一个问题是本网站上提出的以下问题的延伸。

Is there a more elegant way to merge observables when return type is unimportant?

我有一个IObservable<Unit>(比如说X)、一个反应式集合(Y)和一个属性(Z)。返回类型并不重要。我只想在这些变化时订阅。

我知道如何使用Observable.Merge 观察所有 3 和 Subscribe,如下所示。

Observable.Merge(X, Y.Changed, ObservableForProperty(Z).Select(_ => Unit.Default)).Subscribe(..)

而且它有效。

但是,当我尝试使用WhenAny(...,....,....).Subscribe() 时,当我的X 更改时,订阅不会被触发。使用WhenAny(...) 而不是Observable.Merge(..) 执行上述操作的语法是什么?

我更喜欢使用WhenAny(....),因为我在其他地方使用ReactiveUI

示例: 假设我有一个派生自 ReactiveObject 的类,具有以下属性。

public class AnotherVM : ReactiveObject
{
    public bool IsTrue
    {
        get { return this.isTrue; }
        set { this.RaiseAndSetIfChanged(x => x.isTrue, ref this.isTrue, value); }
    }

    public IObservable<Unit> Data
    {
        get { return this.data; }
    }

    public ReactiveCollection MyCol
    {
       get { return Mycol; }
    }    
}

public class MyVM : ReactiveObject
{
    MyVM
    {
       // do WhenAny or Observable.Merge here....
    }
}

我想在AnotherVM 类中使用Observable.Merge(..)WhenAny(...)MyVM 类中观察上述属性。我发现当我使用WhenAny(...)Merge(...)MyVM 中订阅上述内容时,当三个属性中的任何一个发生更改时,我并不总是收到通知。

【问题讨论】:

    标签: system.reactive observable reactiveui


    【解决方案1】:

    WhenAny 不是用于监控任意 observable 集合,而是用于监控 ReactiveUI 支持的对象的属性(如 ReactiveObject 或响应式集合)。

    对于组合可观察流中的变化的一般情况,Observable.Merge 是正确的方法。

    编辑

    我注意到您已将 Data 和 MyCol 属性声明为只读。如果你像这样使用Merge

    Observerable.Merge(this.WhenAnyValue(o=>o.IsTrue, v=>Unit.Default),
                       this.Data,
                       this.MyCol.CollectionChanged.Select(v=>Unit.Default))
    

    ...那么您必须小心不要更改支持字段。如果这样做,那么您将丢失事件 - 也许这就是正在发生的事情?

    在这种情况下,您需要将这些属性连接到 RaiseAndSetIfChanged 并使用 Switch 来跟踪 - 例如如果 this.data 可以更改,那么您将需要(我在这里使用 ReactiveUI 5 + .NET 4.5,以防 RaiseAndSetIfChanged 语法看起来很奇怪):

    public IObservable<Unit> Data
    {
        get { return this.data; }
        private set { this.RaiseAndSetIfChanged(ref data, value); }
    }
    

    你的合并会是这样的:

    Observerable.Merge(this.WhenAnyValue(o=>o.IsTrue, v=>Unit.Default),
                       this.WhenAnyObservable(x => x.Data),
                       this.MyCol.CollectionChanged.Select(v=>Unit.Default))
    

    WhenAnyObservable 在概念上等价于:

    WhenAny(x => x.Data, vm => vm.Value).Switch()
    

    当它发生变化时,使用 Switch 翻转到 Data 的最新值。不要忘记使用 setter 来更改数据的值!

    【讨论】:

    • 这是对 WhenAny() 的一个很好的解释,并阐明了用例。但是,我问的原因是因为我有一个 ReactiveObject (Obj) X (IObservable) 和 Y (a bool) 的两个属性,当我使用 Observable.Merge(obj.X,...) 时,只有首先更改为属性 X 被提出。不会引发任何后续更改。 WhenAny(Obj.X, ...) 似乎也在做同样的事情。但是,当我执行 object.X.Subscribe(...) 时,每次 X 更改时都会调用我的订阅。显然,我在使用 Merge 或 WhenAny 时做的不对,这可能是因为我对 ReactiveUI 和 rx 比较陌生!
    • 你认为存储在 obj.X 中的 observable 上的新事件是对 obj.X 的更改吗?这不是 - 至少在WhenAnyMerge 的特定过载会考虑的意义上不是。 WhenAny 将跟踪分配给obj.X 的新可观察流。订阅流收听它的正确方法。也许如果您添加了一些更具体地展示您的场景的代码,那么可能会更容易获得惯用的答案。
    • ReactiveUI 就是将属性更改和命令表示为 Observables,以便您可以利用 Rx - 它并不能真正帮助您处理 现有的 observables。这就是常规 Rx 的用途。
    • 嗨,詹姆斯,感谢您的帮助。这很有帮助,我同意一个例子会很有用。我在原帖中给出了一个小例子。
    • 詹姆斯,谢谢。是的 ReactiveCollection 是 read.write..我只是在示例中排除了它。
    【解决方案2】:

    应该这样做。

    IObservable<Unit> merged =
        Observerable.Merge
        ( this.WhenAnyValue(o=>o.IsTrue, v=>Unit.Default)
        , this.Data
        , this.MyCol.CollectionChanged.Select(v=>Unit.Default)
        )
    

    理论上,您可以编写一个特殊版本的合并,它会忽略可观察对象的类型并返回IObservable&lt;Unit&gt;。然后你可以写

    IObservable<Unit> merged =
        Observerable.MergeToUnit
        ( this.WhenAnyValue(o=>o.IsTrue)
        , this.Data
        , this.MyCol.CollectionChanged
        )
    

    但是你需要许多 MergeToUnit 重载来支持最多 N 个参数。

    WhenAny 与多个对象一起使用的最通用模式是

    Observable.CombineLatest
      ( source0.WhenAnyValue(s=>s.FieldA)
      , source1.WhenAnyValue(s=>s.FieldB)
      , source2.WhenAnyValue(s=>s.FieldC)
      , source3.WhenAnyValue(s=>s.FieldD)
      , (a,b,c,d) => Process(a,b,c,d)
      )
    

    有时习惯使用标准组合子会更好。

    【讨论】:

    • 用一些额外的信息将其中的一些折叠到我的答案中,谢谢布拉德。 +1
    • 太棒了。非常感谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-14
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多