在这个答案的评论中,OP进一步澄清了他的问题:
确实每一个事件都可以被适配到一个
IObservable
上吗?
对于这个问题,基本上是肯定的,但有一些注意事项。同时需要注意的是,反过来不成立 - 请参阅有关反向转换以及结论部分,了解为什么基于附加含义,Observables可能比经典事件更受欢迎。
对于严格的翻译,我们只需将事件(应包括发送者以及参数)映射到
OnNext
调用上即可。
Observable.FromEventPattern帮助方法很好地完成了这项工作,重载返回
IObservable<EventPattern<T>>
,提供了发送对象和
EventArgs
。
注意事项
回想一下Rx语法。这可以表示为
EBNF格式:
Observable Stream = { OnNext }, [ OnError | OnCompleted ]
- 或0个或多个OnNext事件,后跟任意一个OnCompleted或OnError。
这背后隐含的是从单个订阅者的角度来看,事件不会重叠的想法。要明确的是,这意味着订阅者不会同时被调用。此外,其他订阅者可能会在不同的时间以及同时被调用。通常情况下,正是订阅者自己通过以比到达速度更慢的速度处理事件(创建反压)来控制事件流的节奏。在这种情况下,典型的Rx运算符针对单个订阅者排队,而不是阻塞整个订阅者池。相比之下,经典的.NET事件源更倾向于按步骤广播给订阅者,等待所有订阅者完全处理一个事件,然后再继续。这是经典事件的长期假定行为,但实际上并没有规定。
C# 5.0语言规范(这是一个Word文档,请参见1.6.7.4节)和
.NET Framework设计指南:事件设计 对事件行为的说明非常少。该规范观察到:
“提出事件的概念与调用表示事件的委托是完全等价的 - 因此,没有特殊的语言构造用于引发事件。”
C# Programming Guide : Events 部分表示:
当事件有多个订阅者时,事件处理程序在触发事件时同步调用。要异步调用事件,请参见 调用同步方法异步。
因此,传统的事件通常通过在单个线程上调用委托链来顺序发布,但在指南中没有这样的限制 - 偶尔我们会看到并行调用委托的情况 - 但即使在这种情况下,事件的两个实例通常也会依次被触发,即使每个实例都是并行广播的。
我无法在官方规范中找到明确说明事件实例本身必须按顺序被触发或接收的内容。换句话说,没有任何内容表明无法同时触发多个事件实例。
这与 observables 相反,在
Rx 设计指南中明确说明了以下内容:
4.2. 假设观察者实例以串行方式调用
请注意,此语句仅涉及单个订阅者实例的视点,不涉及跨实例并发发送事件的问题(实际上在 Rx 中非常普遍)。
因此,以下是两个要点:
- 虽然 OnNext 概念上表示事件,但经典的 .NET 事件可能会通过并发调用事件而违反 Rx 语法。
- 在负载下,纯 Rx Observables 具有不同的事件传递语义是很常见的,因为反压通常基于每个订阅者而不是每个事件进行处理。
只要您的 API 不会同时引发事件,并且您不关心反压语义,那么通过类似于 Rx 的 Observable.FromEvent 机制的转换到 observable 接口就没问题了。
反向转换
请注意,在经典的 .NET 事件中,OnError 和 OnCompleted 没有类似物,因此没有办法进行反向映射而不需要一些额外的机制和约定使用方式。
例如,可以将 OnError 和 OnCompleted 转换为其他事件 - 但这绝对超出了经典事件范畴。此外,需要在不同处理程序之间使用一些非常麻烦的同步机制;在 Rx 中,一个订阅者可以在另一个订阅者仍在接收 OnNext 事件时收到 OnCompleted - 在经典的 .NET 事件转换中更难实现这一点。
错误
考虑错误情况下的行为:重要的是要区分事件源中的错误和处理程序/订阅者中的错误。 OnError
用于处理前者,在后者的情况下,经典事件和 Rx 都会(并且正确地)崩溃。这方面的错误在任何方向上都不易传递。
结论
.NET 经典事件和 Observables 不是同构的。只要遵循正常的使用模式,从事件转换到 Observable 可以相对容易地完成。也许您的 API 需要 observables 的附加语义,而这些语义无法轻松地用 .NET 事件建模,因此更有意义的是仅使用 Observable - 但这是一种具体考虑而非一般性考虑,并且更多是设计问题而不是技术问题。
作为一般指导,如果可能的话,我建议优先选择经典事件,因为它们被广泛理解和支持,并且可以进行转换 - 但是,如果您需要表示源错误或完成的额外语义,则毫不犹豫地使用 observables,这些额外语义以 OnError 和 OnCompleted 事件的优雅形式呈现。
System.Reactive
和IObservable
是.NET的标准部分,我不会考虑强制消费者依赖于RX。 (对我来说,这有点受到F#标准库对消费IObservable
的良好库支持的影响) - Ruben BartelinkSystem.Reactive
[和IObservable
] 加入核心。正如我所引用的那样,F# 有很好的消费支持,其他语言也可以提供类似的支持。虽然你说得对,RX 是一个不可忽视的问题,但组合事件的方法不止一种(考虑 F# 代理...)。顺便说一句,我并不是在争论你的中心论点,即这是一个意见领域,但我绝对不想要一个程序员.SE的答案。 - Ruben Bartelink