我是Rx的新手。我可以看到使用Hot Observables的一些真正的好处,但是我最近被问到冷可观察和等效可枚举之间有什么区别的问题(参见下面的代码片段)......
var resRx = Observable.Range(1, 10);
var resOb = Enumerable.Range(1, 10);
任何人都可以非常简单地解释两者之间的区别,以及从冷观察者和可枚举者中获得的好处。
答案 0 :(得分:15)
两者之间存在一些差异。
谁控制'枚举'
使用observable(hot或cold),可观察的是确定何时以及在什么线程上返回值。另一方面,可枚举在您请求时获取每个值,并在请求枚举的线程上处理。
代码流
处理枚举通常在每个循环中完成(或偶尔获取枚举数并使用while循环)。您的代码通常会在继续之前处理所有值。 Observable需要回调。阻止进一步执行代码(比如不要退出控制台应用程序)需要额外的代码。对于可观察对象有一些阻塞运算符,例如First
,但它们是例外而不是正常使用的规则。
以这个简单的示例程序为例。使用可枚举的值,所有值都会在继续下一部分之前编写。但是,不能保证可以观察到可观察值。在程序终止之前写入多少个值几乎就是竞争条件。
static void Main(string[] args)
{
var xs = Enumerable.Range(1, 10);
foreach (var x in xs)
{
Console.WriteLine(x);
}
//at this point, all values have been written
var ys = Observable.Range(1, 10);
ys.Subscribe(y => Console.WriteLine(y));
//at this point, no values have been written (in general)
//either add a Console.ReadKey or some sort of wait handle that
//is set in the OnCompleted of the observer to get values
}
异步流程
就像你必须编写额外的代码来阻止和等待一个observable一样,编写一个使用异步进程的IEnumerable需要一些额外的工作。这是两者之间真正发挥作用的地方。
例如,在我目前正在处理的应用程序中,我需要搜索可能连接到串行端口的设备。 IObservable非常适合这种情况,因为它允许我进行回调并在我找到它时通知应用程序,而不必阻塞和操作完成。这个observable可以作为 cold observable,因为除非有订阅者,否则它不会推送数据,并且每个订阅都会获得所有结果。 (与典型的冷可观察性不同,我在订阅之前开始工作,但没有数据丢失,因为它被缓冲到重放主题中。)但是,由于异步,将它转换为Enumerable对我来说没有多大意义。性质。
答案 1 :(得分:6)
您习惯使用的几乎所有Enumerables都是“Cold”Enumerables?为什么?因为如果你在Enumerable.Range上两次使用ForEach,那么你得到2倍的数字。
如果Enumerable.Range是Hot Enumerable,它只会给你一次列表而第二个ForEach就是空的。
对于Rx,Cold Observable意味着每次调用Subscribe(相当于Rx中的ForEach)时,您都会收到一个新的东西列表。像FromEvent这样的Hot Observable不会在每次订阅时为您提供新的事件流,它只是与同一事件流的另一个连接。
虽然Rx的优点是什么?能够将该范围转换为异步请求:
IObservable<Image> fetchImageByIndex(int imageIndexOnSite);
Observable.Range(0, 10)
.SelectMany(x => fetchImageByIndex(x))
.Subscribe(image => saveImageToDisk(image));