在面向.NET 4+的库中公开通知时,IObservable是否应优先于事件

Rub*_*ink 27 .net c# events system.reactive

我有一个.NET库,作为对象模型的一部分将发出某些事件的通知.

在我看来,事件的主要优点是初学者的可接近性(以及某些消费环境中的简单性),主要的负面因素是它们不可组合,因此如果你想做任何有趣的事情而不写代码丛林.Observable.FromEvent

正在解决的问题的本质是事件流量不会特别频繁或大量(它肯定不会尖叫RX),但绝对没有要求支持4.0之前的.NET版本[因此我可以使用内置IObservable接口,System.Reactive不会对消费者造成任何重大依赖].我感兴趣的是一些一般原则一些特定的具体原因,更喜欢IObservablesevent从API设计的角度看小号虽然-无论在哪里,我的具体情况可能在坐event- IObservable频谱.

那么,问题是:

如果我选择最简单的东西并暴露一个event而不是一个IObservable

或者,重申:除了有做一个消费者Observable.FromEvent*能够撰写事件,是真的不是一个单一的理由,更喜欢一个IObservable比一个event的API中暴露的通知是什么时候?

IObservable用于非尖叫-RT内容或编码指南的项目的引用将是理想的但并不重要.


在@Adam Houldsworth的评论中提到的NB,我对.NET 4+库的API表面的具体内容感兴趣,而不是对我们这个时代代表更好的"默认架构"的意见调查: )

注意,在C#中的IObserver和IObservable中已经触及了这个问题,对于Observer vs Delegates,事件IObservable与普通事件或为什么我应该使用IObservable?.由于SRP违规,我提出的问题的方面没有在任何答复中得到解决.另一个略微重叠的问题是.NET Rx优于经典事件?.(使用IObservable而不是事件)[ 使用IObservable而不是事件属于同一类别.

Jam*_*rld 14

在这个答案的评论中,OP将他的问题提炼为:

确实是这样的事情,每个事件总是可以适应成为一个IObservable

对于这个问题,答案基本上是肯定的 - 但有一些警告.还要注意反之亦然 - 请参阅反向转换部分和结论,了解为什么可观测量可能优于经典事件,因为它们可以传达其他含义.

对于严格的翻译,我们需要做的就是将事件(应包括发送者和参数)映射到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#编程指南:活动节说:

当事件具有多个订阅者时,在引发事件时将同步调用事件处理程序.要异步调用事件,请参阅异步调用同步方法.

因此传统上传统的事件通过在单个线程上调用委托链来连续发布,但是指南中没有这样的限制 - 偶尔我们会看到委托的并行调用 - 但即使这样,事件的两个实例通常也会连续引发如果每个人并行播放.

在官方规范中没有任何地方可以找到明确声明事件实例本身必须按顺序引发或接收的任何内容.换句话说,没有任何内容表明不能同时引发多个事件实例.

这与Rx设计指南中明确规定的可观测量相反,我们应该:

4.2.假设以序列化方式调用观察者实例

请注意,此语句仅解决了单个订阅者实例的观点,并且没有说明跨实例同时发送的事件(实际上在Rx中非常常见).

所以两个走道:

  • 虽然OnNext捕捉事件的想法很可能是经典的.NET事件可以通过同时调用事件违反的Rx语法.
  • 纯Rx Observable通常在负载下传递事件时具有不同的语义,因为通常每个订户而不是按事件处理反压.

只要你不在API中同时引发事件,并且你不关心反压语义,那么通过像Rx这样的机制转换到可观察的接口Observable.FromEvent就可以了.

逆转换

在逆向转换中,请注意OnError并且OnCompleted在经典.NET事件中没有模拟,因此如果没有一些额外的机制和约定的用法,就不可能进行反向映射.

例如,一个人可以翻译OnErrorOnCompleted其他事件 - 但这绝对是在经典事件领域之外.此外,在不同的处理程序中需要一些非常笨拙的同步机制; 在Rx中,一个用户很可能接收到一个用户,OnCompleted而另一个用户仍然在接收OnNext事件 - 在经典的.NET事件转换中更难以实现这一点.

错误

考虑错误情况下的行为:将事件源中的错误与处理程序/订阅者中的错误区分开来很重要.OnError是否有处理前者,在后一种情况下,经典事件和Rx简单(和正确)爆炸.这方面的错误确实可以很好地转化为任何一个方向

结论

.NET经典事件和Observables不是同构的.只要您坚持正常的使用模式,您就可以合理地轻松地从事件转换为可观察的事物.可能的情况是,您的API需要使用.NET事件不太容易建模的observable的附加语义,因此仅使用Observable更有意义 - 但这是一个特定的考虑而不是一般的考虑,而更多的是设计问题而非技术问题.

作为一般指导,我建议在可能的情况下优先选择经典事件,因为它们被广泛理解并得到很好的支持并且可以被转换 - 但如果您需要源代码错误或完成的额外语义以优雅的形式表示,请不要犹豫使用observable OnError和OnCompleted事件.


Ped*_*Kid 1

IObservableIEnumerable事件的本质,所以这里唯一的问题是你认为它IObservable会像现在一样成为标准吗IEnumerable?所以是的,如果你认为它只是一个过路的东西,会在未来的使用中落下,event那么它是更可取的。

IObservable比大多数情况下要好event,但我个人认为我会忘记使用它,因为它不太常用,当时间到来时我会忘记它。

我知道我的回答没有多大帮助,但只有时间才能证明 RX 是否会成为标准,我认为它有很好的机会。

[编辑] 使其更加具体。

对于最终用户来说,唯一的区别是一个是接口,另一个不是,这使得接口更易于测试和扩展,因为不同的源可以实现相同的接口。

正如 Adam Houldsworth所说,一个可以轻松地更改为另一个,而不会产生其他差异。

  • @RubenBartelink 只有根据您对使用者需求的了解才能实现具体的原因。我认为重要的是保持一致性。归根结底,如果你要提出事件并且有人想要一个可观察的,他们会围绕它进行探索并继续前进。反之亦然,如果被给予一个可观察值并想要一个事件。我个人认为两者的公共契约是相同/相似的,因此并不重要,这取决于消费方式。那个讨论没有价值,因为我没有提到 Rx 对你的问题至关重要。 (2认同)