IEnumerable <t> .Count()实际上是否适用于IObservable <t>?</t> </t>

时间:2013-11-04 20:40:56

标签: c# system.reactive observer-pattern

有没有一个例子告诉我Observable.Count<TSource> Method实际上是如何运作的?我提出的示例似乎返回一个包含在observable中的计数而不是预期的计数。

例如,我希望从此返回1

System.Diagnostics.Debug.WriteLine((Observable.Return<string>("Hello world!")).Count());

将来会返回1(因为毕竟它是异步序列)?或者我错过了一些基本的东西?在撰写本文时,我实际上假设.Count()会返回T的结果,并且只要结果被推出就会随着时间的推移而增长。真?是。

2 个答案:

答案 0 :(得分:5)

Rx中的聚合运算符与LINQ中的运算方式略有不同 - 它们不会立即返回值,它们返回将来的结果(即我们只能知道Observable后的最终Count是什么完成)。

所以如果你写:

Observable.Return("foo").Count().Subscribe(x => Console.WriteLine(x));
>>> 1
  

因为毕竟它是一个异步序列

这实际上并不完全正确。在这里,一旦有人打电话Subscribe,一切都将立即运行。上面的代码没有任何异步,没有额外的线程,一切都发生在订阅。

答案 1 :(得分:4)

我认为使用一个observable立即返回也使用ass / await语法,就像rasx在评论中所做的那样,这让人感到困惑。

让我们创建一个包含5个元素的流,每秒返回一个然后完成:

private IObservable<long> StreamWith5Elements()
{
    return Observable.Interval(TimeSpan.FromSeconds(1))
                     .Take(5);
}

我们可以使用async / await magic来调用它,就像在这个LINQPad友好示例中一样:

void Main()
{
    CountExampleAsync().Wait();
}

private async Task CountExampleAsync()
{
    int result = await StreamWith5Elements().Count();
    Console.WriteLine(result);
}

但它误导了这里发生的事情 - Count()返回IObservable<int>,但Rx对await非常友好,并将结果流转换为Task<int> - await然后交回该任务的int结果。

当您使用等待IObservable<T>时,您隐含地表示您希望observable使用单个结果调用OnNext(),然后调用OnComplete()。实际发生的是,您将获得一个Task<T>,它返回在流终止之前发送的 last 值。 (类似于AsyncSubject<T>的行为)。

这很有用,因为它意味着任何流都可以映射到Task,但需要仔细考虑。

现在,上面的示例等同于以下更传统的Rx:

void Main()
{
    PlainRxCountExample();
}

private void PlainRxCountExample()
{
    IObservable<int> countResult = StreamWith5Elements().Count();
    countResult.Subscribe(count => Console.WriteLine(count));

    /* block until completed for the sake of the example */
    countResult.Wait();
}

在这里,您可以看到Count()确实返回了一个int流 - 以提供异步计数。它仅在源流完成时返回。

在Rx的早期,Count()实际上是同步的。

然而,这并不是一个非常有用的事态,因为它“退出Monad” - 即将你带出IObservable<T>并阻止你进一步使用Rx操作符。

一旦你开始“在溪流中思考”,Count()的异步性质就非常直观了,因为当然你只能在流完成时提供一个流的计数 - 为什么还要为此而烦恼? :)