所以我使用这种语法(类似于推荐):
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 编程世界中应该如何完成?
为了更清楚地演示问题,我增加了
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;