Functional folds
接下来我将学习折flod(叠相)关功能,这些方法将返回IObservable<T>并产生一个T值。每次对可观测物队列使用这些flod方法时都必须谨慎,因为它们都阻塞线程。你需要谨慎对待阻塞方法的原因是你正从异步范式移动到同步范式,这关系到锁定UI和死锁等问题。之后会有专门讲并发的章节(即将发布的.NET 4.5和RX2将避免这些问题)。
First
First方法返回队列的第一个值:
如果源队列没有给出任何值,则First方法将会抛出异常。你可以用3种方式来解决:
- 使用try/catch。
- 用Take(1)代替,但这是异步且不阻塞的。
- 使用FirstOrDefault代替。FirstOrDefault将阻塞线程直到源产生任何通知。如果消息是一个OnErro将会被抛出,如果是OnNext则会被返回,如果是OnCompleted则返回默认。像之前的方法,我们可以选择返回类型T的默认值,或是自己设置的默认值。
- 特别值得一提的是BehaviorSubject和First()可以很好的配合,因为BehaviorSubject保证了必须有一个消息,无论是value还是错误或者完成,它可以有效的消除First方法对线程的阻塞。
Last
Last和LastOrDefault将会阻塞线程直到源完成并返回最后一个值。如果队列为空,则引发InvalidOperationException,但可以使用LastOrDefault来避免。
Single
Single方法获取队列中的一个值。它与First()或Last()的区别是可以帮助你判断队列是否只有一个值。该方法阻塞线程,直到源生成值,然后完成。如果队列产生任何其他组合消息将抛出异常。这种方法适合与AsyncSubject实例一起工作,因为它们只产生一个值。
Aggregate
Aggregate方法对队列应用一个累加方法(方法的Func类型委托参数)。以基本重载为例,需要提供一个接受当前累计数和队列正在发送的值的方法(Func类型委托),其返回值是更新后的累计值,签名如下:
如果你想以自己的方式累计int值,可以提供自己的方法来进行计算。
下面这个重载有几个问题。首先它要求累计值与队列的value类型相同;其次该重载需要源至少产生1个值,否则将抛出InvalidOperationException。使用Aggregate方法在一个空队列中创建自己的Count或者Sum是可行的,但需要其他重载。该重载需要额外的作为seed(种子)的参数,提供初始的累计值。这个重载也允许累计值与队列的value类型不同。
使用这个重载来根据自定义的求和方法(Func类型委托)进行更新是很方便的,只要添加为0的种子即可。它将在队列为空时返回0,这正是我们想要的效果。
从Aggregate创建Min和Max的例子:
Scan
如果我们需要队列中接收到的值的总和,Aggregate并不合适,同时也不支持无限队列;Scan方法完全适合这种需求。Scan方法和Aggregate方法的签名是相同的,不同的是Scan方法会发送每次调用的结果给累加方法(Func类型委托)。作为一个聚合器,它将一个队列简化为一个单值队列,它是一个返回source sequence(源队列)中每个值的累加值的累加器。在下面的例子中,我们产生了一个运行时总和:
Output:
numbers-->1
sum-->1
numbers-->2
sum-->3
numbers-->3
sum-->6
numbers completed
sum completed
可以与TakeLast().Aggregate()一起使用Scan方法:
下面的两个例子作为获取一个队列运行时的最大值和最小值的练习,要求每次收到一个值小于(或大于)我们当前的累加值(或大于一个Max操作)时我们就应该发布这个值并更新累加值,并且不能出现重复的数值:
运行时最小值的例子:
运行时最大值的例子:
都13了
@Oncle:哈哈,还早还早,这才第2章一半多一点