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

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

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

正在解决的问题的本质是事件流量不会特别频繁或大量(它肯定不会尖叫RX),但绝对没有要求支持4.0之前的.NET版本[因此我可以使用System.Reactive内置的IObservable接口,不会强制对消费者产生任何重大依赖关系]。 我对一些通用指南感兴趣, 但是从API设计的角度来看,更喜欢IObservables不是event的一些具体原因 – 无论我的具体案例可能在哪里 – IObservable频谱。

那么,问题是:

如果我采用最简单的方法并且暴露event而不是IObservable那么对于API消费者来说,是否有任何具体的事情变得更加困难或有问题?

或者,重申:除了消费者必须执行Observable.FromEvent *才能组合事件之外,在API中公开通知时,是否真的没有理由更喜欢IObservable而不是event

使用IObservable进行非尖叫-RX内容或编码指南的项目的IObservable将是理想的,但并不重要。


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

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

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

确实是这样的事情,每个事件总是可以被改编为IObservable吗?

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

对于严格的转换,我们需要做的就是将事件(包括发送者和参数)映射到OnNext调用。 Observable.FromEventPattern帮助器方法很好地解决了这个问题,重载返回IObservable>提供sender对象和EventArgs

注意事项

回想一下Rx语法。 这可以用EBNFforms表示为: Observable Stream = { OnNext }, [ OnError | OnCompleted ] 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这样的机制转换到可观察的接口就可以了。

逆转换

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

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

错误

考虑错误情况下的行为:区分事件源中的错误与处理程序/订阅者中的错误很重要。 OnError可以处理前者,在后一种情况下,经典事件和Rx只是(并且正确地)爆炸。 这方面的错误在任何一个方向上确实都很好。

结论

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

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

我一直在阅读很多关于Reactive扩展的内容,然后才开始蘸我的脚趾,经过一些粗略的开始,我发现它们非常有趣和有用。

Observables扩展具有这个可爱的可选参数,您可以通过自己的时间管理器,有效地让您操纵时间 。 在我的情况下,它帮助了很多,因为我做了一些时间相关的工作(每十分钟检查一次这个webservice,每分钟发送一封电子邮件等),它使测试变得轻而易举。 我会将TestScheduler插入正在测试的组件中,并快速模拟一天的事件。

因此,如果您的库中有一些工作流程,其中时间在业务流程中起作用,我建议您使用Observables作为输出。

但是,如果您只是直接回应用户输入事件,我认为值得增加用户的复杂性。 如您所知,如果需要,他们可以将事件包装到自己的Observable中。

你可以吃蛋糕,也可以吃它,虽然这意味着更多的工作; 提供一个外观,通过从您的事件中创建所述Observable,将您的事件库转换为Observable-fueled。 或者以另一种方式执行:拥有一个可选的外观,可以监视您的observable并在触发时引发经典事件。

在我看来,在处理被动扩展时需要采取一个非常重要的技术步骤,在这种情况下,它可能归结为您的API消费者最容易使用的

IObservableIEnumerable事件,所以这里唯一的问题是你认为IObservable将成为标准,因为IEnumerable现在是如此优选,如果你认为它只是一个将来会落入未来使用event东西。

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

我知道我的答案并没有很大的帮助,但只有时间会certificateRX是否会成为标准,我认为它有很好的机会。

[编辑]使其更具体。

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

正如Adam Houldsworth所说,一个人可以很容易地改变另一个人而不会产生其他任何不同。

要解决您的标题问题:

不,他们不应该首选,因为它们存在于.NET 4中并且可以使用。 偏好取决于预期用途,因此一揽子偏好是没有根据的。

也就是说,我倾向于将它们作为传统C#事件的替代模型。

正如我在整个问题中所评论的那样,使用IObservable接近API有很多辅助优势,其中最重要的是外部支持和最终消费者可用的选择范围。

解决你的内心问题:

我相信在API中暴露事件或IObserable之间几乎没有什么困难,因为在这两种情况下都有一条路由到另一条路径。 这会在您的API上添加一层,但实际上这是一个您也可以发布的图层。

我认为选择一个而不是另一个不会成为某人选择使用或不使用您的API的决定性原因的一部分。

解决您重申的问题:

原因可能在于为什么首先存在Observable.FromEvent 🙂 IObservable在.NET的许多地方获得支持,用于反应式编程并构成许多流行库(Rx,Ix,ReactiveUI)的一部分,并且与LINQ和IEnumerable很好地互操作,并进一步与TPL和TPL DataFlow类似。

可观察模式的非Rx示例,因此不是特定的IObservable将是XAML应用程序的ObservableCollection