Difference between cold observable in RX and normal Enumerable
I am new to Rx. I can see some real benefits of using Hot Observables, however I was recently asked the question on what the diff开发者_开发技巧erence was between a cold observable and an equivalent enumerable (see code snippet below)...
var resRx = Observable.Range(1, 10);
var resOb = Enumerable.Range(1, 10);
Can anyone explain very simply what the difference is between the two and what benefit I would get from the cold observable vs the enumerable.
There are several differences between the two.
Who controls the 'enumeration'
With the observable (hot or cold), it is the observable that determines when and on what thread the values are returned. An enumerable on the other hand gets each value when you request it and is processed on the thread requesting the enumeration.
Code Flow
Processing enumerables is typically done in a for each loop (or occasionally getting the enumerator and using a while loop). Your code will typically process all values before continuing. Observables require a callback. Blocking further execution of your code (say to keep from exiting a console app) requires extra code on your part. There are some blocking operators for observables, such as First
, but they are the exception rather than the rule for normal usage.
Take this trivial sample program as an example. With the enumerable, all the values get written before continuing on to the next part. The values from the observable, however, are not guaranteed to ever be written. How many values gets written before the program terminates is pretty much a race condition.
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
}
Asynchronous processes
Much like you have to write extra code to block and wait for an observable, writing an IEnumerable that uses an asynchronous process requires some extra work. Here is where the difference between the two really comes into play.
For example, in an application I am currently working on, I need to search for devices that may be attached to a serial port. An IObservable is a good fit for this because it allows me to take a callback and notify the application for each device whenever I find it without having to block and when the operation is complete. This observable qualifies as a cold observable because it will not push data out unless there is a subscriber, and each subscription gets all the results. (Unlike the typical cold observable, I start the work before a subscription, but no data is lost because it gets buffered into a replay subject.) However, it would not make much sense to me to convert it to an Enumerable because of the asynchronous nature.
Almost all Enumerables that you are used to are "Cold" Enumerables? Why? Because if you were to ForEach over the Enumerable.Range twice, you'd get 2x the numbers.
If Enumerable.Range were a Hot Enumerable, it would only give you the list once and the 2nd ForEach would just be empty.
For Rx, a Cold Observable means that every time you call Subscribe (the equivalent of ForEach in Rx), you'll receive a new list of stuff. Hot Observables like FromEvent aren't going to give you a new stream of events every time you Subscribe, it's just going to be another connection to the same event stream.
What's the advantage of Rx here though? Being able to convert that range into async requests:
IObservable<Image> fetchImageByIndex(int imageIndexOnSite);
Observable.Range(0, 10)
.SelectMany(x => fetchImageByIndex(x))
.Subscribe(image => saveImageToDisk(image));
精彩评论