Rx.NET中Select不触发订阅但SelectMany触发的问题咨询
Rx中Select不触发内部Observable订阅、SelectMany触发的核心原因
两种操作符的本质行为差异
Select是纯投影操作符
它只会对上游推送的每个值执行你传入的投影函数,把函数返回值直接作为普通数据往下游传递,不会对返回值做任何额外处理。
你写的_subject.Select(_ => GetObservable()).Subscribe()逻辑中:- 最外层的订阅只作用于
_subject本身 - 当
_subject.OnNext(1)触发时,确实会调用GetObservable()拿到一个IObservable<Unit>实例 - 但这个实例只是被当作一个普通的对象值往下游传递,没有任何代码对它执行订阅操作
而Observable.Create传入的委托,只有当返回的可观察序列被实际订阅时才会执行,既然没有订阅动作,内部的Debug.WriteLine("SUBSCRIBED")自然不会运行。
- 最外层的订阅只作用于
SelectMany是打平嵌套序列的操作符
它的设计目标就是处理「投影结果本身是可观察序列」的场景,内置了订阅内部序列的逻辑:- 上游推送值时,同样调用投影函数拿到内部的
IObservable<T>实例 - 操作符内部会自动订阅这个拿到的内部序列
- 把内部序列推送的所有值、产生的所有事件都转发到下游
你找不到订阅动作的位置是因为这段订阅逻辑完全封装在SelectMany的内部实现里,不需要你手写。正因为它完成了对GetObservable()返回序列的订阅,Observable.Create的委托才会被执行,调试输出才会命中。
- 上游推送值时,同样调用投影函数拿到内部的
可以用一个通俗的类比理解:把
IObservable比作一个消息群,Observable.Create里的委托相当于入群触发的欢迎语。
- 用
Select相当于每次有人给你发群邀请链接,你直接把链接转发给别人,自己从来不点链接进群,自然收不到入群欢迎语,也看不到群里的消息。- 用
SelectMany相当于每次收到群邀请链接,你自己先点链接入群(完成订阅),再把群里的所有消息转发给别人,入群的时候自然会触发欢迎语。
等价写法验证
如果你想保留Select的写法同时达到和SelectMany一样的效果,需要额外加Merge操作符来打平嵌套的序列,由Merge来完成内部序列的订阅:
// 这行和 _subject.SelectMany(_ => GetObservable()).Subscribe() 效果完全一致 _subject.Select(_ => GetObservable()).Merge().Subscribe();
如果你的业务需求是每次_subject推送值时都执行GetObservable()里定义的异步/响应式逻辑,使用SelectMany是符合Rx设计规范的正确写法。
内容的提问来源于stack exchange,提问作者Flack
相关产品推荐
相关产品推荐

