使用 WhenAnyValue 和 null 传播时如何避免过时的值?

问题描述 投票:0回答:1

所以我使用这种语法(类似于推荐):

WhenAnyValue(o => o.Foo, o => o.Foo!.Bar, (foo, bar) => (foo, bar))

订阅我的视图模型的

Foo
Bar
的更改:

public class VM : ReactiveObject
{
    [Reactive]
    public Foo? Foo { get; set; }
}

public class Foo : ReactiveObject
{
    [Reactive]
    public int Bar { get; set; }
}

但是,这会使用 stale 值多次运行订阅者。在

Foo = null
订阅者被调用之前的值
Bar
后,在
Foo = new()
订阅者被调用两次:使用之前的值
Bar
和正确的值。这可以通过以下代码进行演示:

var vm = new VM() { Foo = new Foo { Bar = 1 } };

vm.WhenAnyValue(o => o.Foo, o => o.Foo!.Bar, (foo, bar) => (foo, bar))
.Subscribe(o => Console.WriteLine($"{o.foo?.GetHashCode()} {o.bar}"));

vm.Foo = null; // #1

vm.Foo = new Foo { Bar = 2 }; // #2

输出如下所示:

58225482 1
 1                   << stale Bar
32347029 1           << stale Bar
32347029 2           << extra call

相反,我想要这样的东西:

58225482 1
 0
32347029 2

换句话说,如果可能的话,我不想拥有陈旧值和额外的订户呼叫。

订阅者内部的

Foo == null
检查将解决#1。但也许对于这样的问题有不同的 rx 解决方案?或者以某种方式将空检查移到订阅者之外也可以吗?

对于#2,我只知道存储先前值

Foo
的解决方法,并在先前值是
null
时忽略第一次调用。再次强调,这是一种程序化的编程方式。在 rx 编程世界中应该如何完成?

c# reactiveui
1个回答
0
投票

为了更清楚地演示问题,我增加了

Foo
属性的数量:

public class Foo : ReactiveObject
{
    [Reactive]
    public int Bar { get; set; }
    [Reactive]
    public int Baz { get; set; }
    [Reactive]
    public int Qux { get; set; }
}

然后输出

var vm = new VM();

vm.WhenAnyValue(o => o.Foo, o => o.Foo!.Bar, o => o.Foo!.Baz, o => o.Foo!.Qux,
    (foo, bar, baz, qux) => (foo, bar, baz, qux)).Subscribe(o =>
{
    Console.WriteLine($"{o.foo?.GetHashCode()} {o.bar} {o.baz} {o.qux}");
});

vm.Foo = new();
vm.Foo = null;
vm.Foo = new Foo { Bar = 1, Baz = 2, Qux = 3 };
vm.Foo = null;
vm.Foo = new();

将包含许多陈旧的状态:

30631159 0 0 0
 0 0 0
56680499 0 0 0
56680499 1 0 0
56680499 1 2 0
56680499 1 2 3
 1 2 3
6444509 1 2 3
6444509 0 2 3
6444509 0 0 3
6444509 0 0 0

这是订户的固定版本:

{
    // discard stale state
    if (o.foo != null && (o.bar != o.foo.Bar || o.baz != o.foo.Baz || o.qux != o.foo.Qux))
        return;

    // Console.WriteLine($"{o.foo?.GetHashCode()} {o.bar} {o.baz} {o.qux}");
    // use actual null propagated values!
    Console.WriteLine($"{o.foo?.GetHashCode()} {o.foo?.Bar} {o.foo?.Baz} {o.foo?.Qux}");
}

现在输出符合预期,每次更改一行:

30631159 0 0 0

56680499 1 2 3

6444509 0 0 0

换句话说:

  • 空传播的订阅者总是需要检查以丢弃过时的状态;
  • 空传播值不应该在订阅者内部使用,它们的目的是检测它们的个体变化,例如当
    vm.Foo.Bar = 123;
© www.soinside.com 2019 - 2024. All rights reserved.