阅读msdn关于Reactive Extensions等等,我发现了一条建议说我不应该实现IObservable,而是使用Observable.Create ......当我读到这篇文章时,我的项目已经有{{ 1}}类,我用它作为IObservable源,我想把事件转换成Observables。
我已经阅读了System.Reactive中的ObservableImplementation<T>
实现,但我发现他们的代码和我的代码之间没有任何重大差异。那么实现IObservable有什么问题?我可以添加自己的属性,依此类推......
为了丰满起见,这是我的实施,请告诉我,如果我做错了什么!
AbstractObservable<T>
答案 0 :(得分:22)
我们不建议人们实施IObservable&lt; T&gt;的原因有几个。直接
一个是缺乏对违反观察者语法的保护。例如,您的序列可能会在OnCompleted调用后显示OnNext调用的行为,该调用无效。 Observable.Create&lt; T&gt;方法和ObservableBase&lt; T&gt;基类型通过在接收到终端消息时自动分离观察者来处理这个问题。因此,即使你的代码做错了事,观察者也看不到错误的序列。
顺便说一句,这与C#中的迭代器类似。实现IEnumerable&lt; T&gt;手动应该是这样当枚举器的MoveNext返回false(类似于OnCompleted)时,后续调用不会改变主意并开始返回true(类似于OnNext):
如果MoveNext传递集合的末尾,则枚举数位于集合中的最后一个元素之后,MoveNext返回false。当枚举器处于此位置时,后续对MoveNext的调用也会返回false,直到调用Reset。 (来源:MSDN)
在C#2.0或VB 11.0中使用迭代器时,会为您解决此类问题。这类似于我们的Observable.Create&lt; T&gt;方法和ObservableBase&lt; T&gt;基础类型。
与上述讨论相关的原因是清理。从订阅上的Dispose调用返回后,观察者是否会再看不到任何消息?在向观察者发送终端消息后,是否会自动调用相关订阅的Dispose逻辑?两者都是非常重要的,所以我们的基础实现会解决这个问题。
另一个原因与我们的CurrentThreadScheduler有关,确保在调度程序上运行时,Subscribe调用可以是异步的。基本上,我们需要检查在调用Subscribe期间是否需要在当前线程上安装trampoline。我们不希望每个人都知道这一点并做正确的事。
在你的特殊情况下 - 正如其他人所注意到的那样 - 你正在建立一个非常重要的主题。要么只是使用我们的一个主题,要么用你自己的类型包含它(例如,如果你希望发送“观察者”一方可以被接收“可观察”方面的其他方访问。)
答案 1 :(得分:9)
你不应该实现IObservable<T>
的原因与你通常不实现IEnumerable<T>
的原因相同,是因为有人很可能已经构建了你想要的东西。在这种情况下,您基本上已经重新实现了Subject<T>
。
编辑:关于评论中的懒惰问题,我会这样实现:
var lazyObservable = Observable.Create<TFoo>(subj => { /*TODO: Implement Me to load from reflection*/ })
.Multicast(new Subject<TFoo>()) // This means it'll only calc once
.RefCount(); // This means it won't get created until someone Subscribes
答案 2 :(得分:6)
来自Rx团队的最近blog post包含三个原因。因为这是一篇冗长的帖子,所以我复制了相关部分。
执行合同
Observable.Create接受将成为核心的单个委托 在生成的IObservable上实现Subscribe方法 实现。我们围绕这个代表做了一些巧妙的包装 执行观察员合同,以及其他事情(这就是你的原因 不应该自己实现界面。)
一次性包装
返回的一次性物品周围有一个小包装,用于确保 从Dispose返回后,观察者不再被调用 呼叫,即使调度程序可能不是一个好的停止点 然而。 (你应该永远不要实现IObservable的另一个原因 手工界面。哦,顺便说一句,还有更多!)
完成后自动处理
这里的兴趣点是应用的自动处置行为 发送OnCompleted下游时的源订阅。 (这是手动实施的另一个原因 强烈建议不要使用IObservable。使用时 Observable.Create,我们会为您解决这个问题。)