Observable.Do 从不触发,即使底层 IObservable 发生变化
Observable.Do never fires, even when the underlying IObservable changes
我在 ReactiveUI 和 System.Reactive.Linq
中学习了一些速成课程,因为我发现我需要的 UI 库将它用于所有事情。大部分看起来都很好理解,但是有一个操作没有做任何事情。
我有一个控件,我需要在两个地方使用它的值。我有一个 IObservable<T>
代表它的值,我使用如下:
情况 1:我需要将一个值与另一个可观察值相结合,从而将一个值提供给另一个可观察值。所以我用
Observable.CombineLatest(myObservable, otherObservable, (m, o) => ProduceValue(m, o))
这更新完全符合预期。由此,我知道 myObservable
正在正确触发更新。
案例2:
我需要在其他地方,在不可观察的上下文中使用这个值。所以:myObservable.Do(v => UpdateViewModelWith(v))
。 这永远不会触发。 我已经通过在 lambda 中放置一个断点并在调试器下 运行 对其进行验证。
从案例 1 我知道 observable 正在正确触发。据我了解,可观察对象在概念上很像事件,(有一堆机制让它们感觉更像 IEnumerable
s,)并且像事件完全能够接受多个听众,所以事实上有两个他们应该不是问题。 (通过更改两个侦听器的设置顺序进行验证,这不会对观察到的行为产生任何变化。)那么是什么导致案例 2 永远不会 运行?
我猜你正在使用一个没有任何订阅者的可观察对象,这意味着它永远不会触发并且 Do
永远不会被激活。
你想要的功能很可能是IObservable<T>.Subscribe<T>(Action<T> action)
:
myObservable.Subscribe(v => UpdateViewModelWith(v))
Do
和Subscribe
的区别在于Do
是副作用。这是在管道一侧发生的事情:“给我一些值,哦,顺便说一下,在旁边做这个。” 但是如果没有管道,这些值永远不会发送。
另一方面Subscribe
注册一个观察者。它实际上创建了一个管道,通过告诉 observable:“嘿有人在看,发送东西!”
为了更好地理解它的含义,您还可以查看 return 类型:
Do
return 一个 IObservable<T>
,因为未使用可观察对象。
Subscribe
return 是一个 IDisposable
,因为它注册了一个观察者。
重要提示:一定要包含using System
,否则你只会看到IObservable<T>.Subscribe<T>(IObserver<T> observer)
.
顺便说一句,我今天早上也对 Do 和 Subscribe 感到困惑,并在非常好的 Reactive Slack 上得到了帮助。如果您正在使用 Rx 或 ReactiveUI,我强烈建议您加入!
@Erwin 的回答很接近。只是为了详细说明:
Do
,像大多数 Rx 相关函数一样,是一个 运算符 。没有订阅,运营商什么都不做。例如:
var source = Observable.Range(0, 5);
var squares = source.Select(i => i * i);
var logged = squares.Do(i => Console.WriteLine($"Logged Do: {i}));
var sameThingChained = Observable.Range(0, 5)
.Select(i => i * i)
.Do(i => Console.WriteLine($"Chained Do: {i}));
//until here, we're in no-op land.
Range
、Select
和 Do
都是运算符,它们在没有订阅的情况下什么都不做。如果你想做任何事情,你需要订阅。
var subscription = logged.Subscribe(i => Console.Writeline($"Subscribe: {i}");
输出:
Logged Do: 0
Subscribe: 0
Logged Do: 1
Subscribe: 1
Logged Do: 4
Subscribe: 4
Logged Do: 9
Subscribe: 9
Logged Do: 16
Subscribe: 16
通常,副作用代码(非功能代码)应该驻留在 Subscribe
函数中。 Do
最好用于 logging/debugging。因此,如果我想记录原始的非平方整数,我可以执行以下操作。
var chainedSub = Observable.Range(0, 5)
.Do(i => Console.WriteLine($"Original int: {i}"));
.Select(i => i * i)
.Subscribe(i => Console.Writeline($"Subscribe: {i}");
在纯 Rx.NET 中(没有 ReactiveUI),只有一种方法可以获得订阅:各种 Subscribe
重载。然而,ReactiveUI 确实有一堆函数可以自己创建订阅,所以你不必处理它们(比如 ToProperty
)。如果您使用的是 ReactiveUI,那么这些可能是比 Subscribe
.
更好的选择
我在 ReactiveUI 和 System.Reactive.Linq
中学习了一些速成课程,因为我发现我需要的 UI 库将它用于所有事情。大部分看起来都很好理解,但是有一个操作没有做任何事情。
我有一个控件,我需要在两个地方使用它的值。我有一个 IObservable<T>
代表它的值,我使用如下:
情况 1:我需要将一个值与另一个可观察值相结合,从而将一个值提供给另一个可观察值。所以我用
Observable.CombineLatest(myObservable, otherObservable, (m, o) => ProduceValue(m, o))
这更新完全符合预期。由此,我知道 myObservable
正在正确触发更新。
案例2:
我需要在其他地方,在不可观察的上下文中使用这个值。所以:myObservable.Do(v => UpdateViewModelWith(v))
。 这永远不会触发。 我已经通过在 lambda 中放置一个断点并在调试器下 运行 对其进行验证。
从案例 1 我知道 observable 正在正确触发。据我了解,可观察对象在概念上很像事件,(有一堆机制让它们感觉更像 IEnumerable
s,)并且像事件完全能够接受多个听众,所以事实上有两个他们应该不是问题。 (通过更改两个侦听器的设置顺序进行验证,这不会对观察到的行为产生任何变化。)那么是什么导致案例 2 永远不会 运行?
我猜你正在使用一个没有任何订阅者的可观察对象,这意味着它永远不会触发并且 Do
永远不会被激活。
你想要的功能很可能是IObservable<T>.Subscribe<T>(Action<T> action)
:
myObservable.Subscribe(v => UpdateViewModelWith(v))
Do
和Subscribe
的区别在于Do
是副作用。这是在管道一侧发生的事情:“给我一些值,哦,顺便说一下,在旁边做这个。” 但是如果没有管道,这些值永远不会发送。
另一方面Subscribe
注册一个观察者。它实际上创建了一个管道,通过告诉 observable:“嘿有人在看,发送东西!”
为了更好地理解它的含义,您还可以查看 return 类型:
Do
return 一个IObservable<T>
,因为未使用可观察对象。Subscribe
return 是一个IDisposable
,因为它注册了一个观察者。
重要提示:一定要包含using System
,否则你只会看到IObservable<T>.Subscribe<T>(IObserver<T> observer)
.
顺便说一句,我今天早上也对 Do 和 Subscribe 感到困惑,并在非常好的 Reactive Slack 上得到了帮助。如果您正在使用 Rx 或 ReactiveUI,我强烈建议您加入!
@Erwin 的回答很接近。只是为了详细说明:
Do
,像大多数 Rx 相关函数一样,是一个 运算符 。没有订阅,运营商什么都不做。例如:
var source = Observable.Range(0, 5);
var squares = source.Select(i => i * i);
var logged = squares.Do(i => Console.WriteLine($"Logged Do: {i}));
var sameThingChained = Observable.Range(0, 5)
.Select(i => i * i)
.Do(i => Console.WriteLine($"Chained Do: {i}));
//until here, we're in no-op land.
Range
、Select
和 Do
都是运算符,它们在没有订阅的情况下什么都不做。如果你想做任何事情,你需要订阅。
var subscription = logged.Subscribe(i => Console.Writeline($"Subscribe: {i}");
输出:
Logged Do: 0
Subscribe: 0
Logged Do: 1
Subscribe: 1
Logged Do: 4
Subscribe: 4
Logged Do: 9
Subscribe: 9
Logged Do: 16
Subscribe: 16
通常,副作用代码(非功能代码)应该驻留在 Subscribe
函数中。 Do
最好用于 logging/debugging。因此,如果我想记录原始的非平方整数,我可以执行以下操作。
var chainedSub = Observable.Range(0, 5)
.Do(i => Console.WriteLine($"Original int: {i}"));
.Select(i => i * i)
.Subscribe(i => Console.Writeline($"Subscribe: {i}");
在纯 Rx.NET 中(没有 ReactiveUI),只有一种方法可以获得订阅:各种 Subscribe
重载。然而,ReactiveUI 确实有一堆函数可以自己创建订阅,所以你不必处理它们(比如 ToProperty
)。如果您使用的是 ReactiveUI,那么这些可能是比 Subscribe
.