Since I’m having a cold Observable here and I subscribe to “grouped” several times, why do I NOT need Publish here? I would have expect it to bring up unwanted results when I run it but to my surprise it works with and without Publish. Why is that?
var subject = new List<string>
{
"test",
"test",
"hallo",
"test",
"hallo"
}.ToObservable();
subject
.GroupBy(x => x)
.SelectMany(grouped => grouped.Scan(0, (count, _) => ++count)
.Zip(grouped, (count, chars) => new { Chars = chars, Count = count }))
.Subscribe(result => Console.WriteLine("You typed {0} {1} times",
result.Chars, result.Count));
// I Would have expect that I need to use Publish like that
//subject
// .GroupBy(x => x)
// .SelectMany(grouped => grouped.Publish(sharedGroup =>
// sharedGroup.Scan(0, (count, _) => ++count)
// .Zip(sharedGroup, (count, chars) =>
// new { Chars = chars, Count = count })))
// .Subscribe(result => Console.WriteLine("You typed {0} {1} times",
// result.Chars, result.Count));
Console.ReadLine();
EDIT
As Paul noticed since we are subscribing to the underlying cold observable twice, we should be going over the sequence twice. However, I had no luck to make this effect visible. I tried to insert debug lines but for example this prints “performing” just once.
var subject = new List<Func<string>>
{
() =>
{
Console.WriteLine("performing");
return "test";
},
() => "test",
() => "hallo",
() => "test",
() => "hallo"
}.ToObservable();
subject
.Select(x => x())
.GroupBy(x => x)
.SelectMany(grouped => grouped.Scan(0, (count, _) => ++count)
.Zip(grouped, (count, chars) => new { Chars = chars, Count = count }))
.Subscribe(result => Console.WriteLine("You typed {0} {1} times",
result.Chars, result.Count));
I wonder if we can make the effect visible that we are dealing with an cold observable and are not using Publish(). In another step I would like to see how Publish() (see above) makes the effect goes away.
EDIT 2
As Paul suggested, I created a custom IObservable<string> for debugging purposes. However, if you set a breakpoint in it’s Subscribe() method you will notice that it’s just going to be hit once.
class Program
{
static void Main(string[] args)
{
var subject = new MyObservable();
subject
.GroupBy(x => x)
.SelectMany(grouped => grouped.Scan(0, (count, _) => ++count)
.Zip(grouped, (count, chars) => new { Chars = chars, Count = count }))
.Subscribe(result => Console.WriteLine("You typed {0} {1} times",
result.Chars, result.Count));
Console.ReadLine();
}
}
class MyObservable : IObservable<string>
{
public IDisposable Subscribe(IObserver<string> observer)
{
observer.OnNext("test");
observer.OnNext("test");
observer.OnNext("hallo");
observer.OnNext("test");
observer.OnNext("hallo");
return Disposable.Empty;
}
}
So for me the question is still open. Why do I not need Publish here on this cold Observable?
You’re only using your List-based source once, so you won’t see duplicate subscription effects there. The key to answering your question is the following observation:
Internally, GroupBy keeps a Dictionary<K, ISubject<T>>. Whenever a message comes in, it gets sent into the subject with the corresponding key. You’re subscribing twice to the grouping object, which is safe, as the subject decouples the producer from the consumer.