Can anyone tell me what causes this seemingly odd Do behavior in the following code? I would expect the Do handler to be called once per OnNext.
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using NUnit.Framework;
[TestFixture]
public class WhyDoesDoActSoWierd
{
[Test]
public void ButWhy()
{
var doCount = 0;
var observable = new Subject<int>();
var stream = observable.Do( x => doCount++ );
var subs =
(from x in stream
where x % 2 == 1
from y in stream
where y % 2 == 0
&& y == x + 1
select new { x, y })
.Subscribe( x => Console.WriteLine( "{0}, {1}", x.x, x.y ) );
observable.OnNext( 1 );
// doCount == 1
observable.OnNext( 2 );
// doCount == 3
observable.OnNext( 3 );
// doCount == 5
observable.OnNext( 4 );
// doCount == 8
observable.OnNext( 5 );
// doCount == 11
observable.OnNext( 6 );
// doCount == 15
Assert.AreEqual( 6, doCount );
}
}
The behaviour here is perfectly normal. The reason is that you don’t just have one subscription, you have many. And because of the cartesian product between the two observables in the query you have a larger number of
Dothan you might otherwise expect.Let’s look at an alternative (but similar) query to yours.
The output from this is:
There’s the initial dump of
doCountX = 0, doCountY = 0which is to be expected as this call todump()occurs before any calls toOnNext.But when we get the first call to
OnNextwe don’t get a value produced by the query because the secondstreamYobservable hasn’t yet been subscribed to.It’s only when
OnNextis called the second time do we get a value from the query which happens to be the firstOnNextvalue paired with the second. Now this also creates a new subscriptions tostreamY, waiting for the next value.So we’ve now got the first two values from
streamXwaiting for the next value from the sequence. So whenOnNext(3)is called we get two results.Each time this happens you can see the number of
Docalls incrementingdoCountYjust keep going up.In fact, given this very plain
SelectManyquery the formula is:So with 6 values produced via
OnNextyou getdoCountYequal to6 * 5 / 2or15.Running with 10 values gives
10 * 9 / 2or45values.So a
SelectManyin fact does many more subscriptions than you might think. This is often why you would generally only use it to chain together observables that only produce a single value each to prevent the exponential explosion of subscriptions.Does it make sense now?