有没有一个例子告诉我Observable.Count<TSource> Method
实际上是如何运作的?我提出的示例似乎返回一个包含在observable中的计数而不是预期的计数。
例如,我希望从此返回1
:
System.Diagnostics.Debug.WriteLine((Observable.Return<string>("Hello world!")).Count());
将来会返回1
(因为毕竟它是异步序列)?或者我错过了一些基本的东西?在撰写本文时,我实际上假设.Count()
会返回T
的结果,并且只要结果被推出就会随着时间的推移而增长。真?是。
答案 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()的异步性质就非常直观了,因为当然你只能在流完成时提供一个流的计数 - 为什么还要为此而烦恼? :)