何时使用IEnumerable而不是IObservable?

17

如何确定一个方法应该返回 IEnumerable<T> 还是 IObservable<T>

为什么我会选择一种范式而不是另一种?


10
这就像问如何决定一个方法应该返回List<int>还是DateTime... 这些类型在语义上没有关联。 - Matthew Watson
2
IEnumerable 用于从集合中 拉取 数据, IObservable 则是用于订阅 推送 的数据。 - Nick
3
它们并不都是枚举。IEnumerable基本上是“给我下一个项”,IObservable是“嘿,看那里,有下一个项”。 - Dirk
@user2346896 IEnumerable<T> 表示一个独立项的序列;IObservable<T> 表示与单个项相关联的时间依赖性变更事件序列。 - Matthew Watson
7
我认为IObservable<T>和IEnumerableIObservable<T>在语义上非常相关。它们在许多方面可能是相对的,但这并不意味着它们没有联系。事实上,Eric Meijer就这一点做了几次演讲,其中一个视频可以在这里找到:http://channel9.msdn.com/Shows/Going+Deep/Expert-to-Expert-Brian-Beckman-and-Erik-Meijer-Inside-the-NET-Reactive-Framework-Rx。这也不是一个愚蠢的问题。在许多情况下,选择其中之一对系统设计有深远的影响。 - James World
显示剩余4条评论
4个回答

28

类型

  • IEnumerable<T>
    • 你可以重复从 T 的序列中"拉取"
  • IObservable<T>
    • 一系列的 T 正在被"推送"给你

为什么要选择其中一种范式而不是另外一种?

通常情况下,你不会主动选择其中一种范式。一般来说,一种范式自然地凸显出来是正确的选择,而另外一种则没有任何意义。

举例

考虑以下例子:

  • 一个庞大的 CSV 文本文件,每行都有一个项目,你想逐个处理它们而不将整个文件一次性加载到内存中:IEnumerable<List<string>>

  • 你正在运行一个 HTTP web 服务器:IObservable<WebRequest>

  • 你希望以广度优先方式获取树数据结构的节点:IEnumerable<Node>

  • 你正在响应用户界面按钮点击:IObservable<Click>

在这些例子中(特别是 IObservable<T> 情况下),使用另一种类型就没有意义。

但如果我想要使用"另一个"类型呢...

IObservable<T> 转为 IEnumerable<T>

如果某个东西自然地属于 IObservable<T>,但你想将其视为 IEnumerable<T> 处理,你可以使用以下方法:

IEnumerable<T> Observable.ToEnumerable(this IObservable<T>)
  • TIObservable<T>“推送”时,它被发送到队列中。
  • 当您尝试从IEnumerable<T>“拉取”T时,它会阻塞直到队列不为空,然后出队。

IEnumerable<T>转换为IObservable<T>

如果某个东西本来就是IEnumerable<T>但您想将其处理为IObservable<T>,则可以使用此方法:

IObservable<T> Observable.ToObservable(this IEnumerable<T>)
  • .NET线程池中的一个线程将会反复尝试从IEnumerable<T>中“拉”出一个T
  • 每当它“拉”出一个T时,它会通过IObservable<T>“推”给你。

8
如果您想在方便的时间将数据“推送”给方法调用者,请使用IObservable<T>。通过调用已经通过Subscribe()注册了兴趣的Observers上的OnNext()来实现此操作。
如果希望方法调用者可以在他们方便的时候“拉取”数据,使用IEnumerable<T>。通过调用GetEnumerator()获取IEnumerator,并调用MoveNext()Currentforeach就是这样编译的)。
更新:也许最好举些例子:
银行货币价格返回函数是IObservable<T>的良好候选者。这里信息是时间关键性的。作为数据的服务器,您需要尽快将其传递给客户端。客户端不知道何时准备好该数据。所以你要推送它给他们。
返回特定金融工具的有效交易日并用于填充日历控件中的黑名单天数的函数是IEnumerable的良好候选者。这个数据很少更改,并且客户端(设置控件)更喜欢以他们自己的节奏消费它。

这是正确的,但我为什么要选择一个范式而不是另一个呢? - jmcg

5

IObservable<T>是一个专门用于发布/订阅和其他模式的接口。在需要返回IObservable<T>时,您会知道,如果没有特定的需求,则应返回IEnumerable<T>

评论中的问题更新

基本上,IObservable<>使用“推”机制,而IEnumerable<>使用“拉”机制。如果你只需要获取数据列表而不需要通知订阅者进行更改(推),则可以使用IEnumerable。如果订阅者(窗口,控件,其他客户端程序/例程等)需要了解数据何时更改,则使用IObservable<>。一个例子是使用IObservable从子线程更新Windows程序中的主UI线程(通常情况下,这是不可能的,除非进行繁琐的脚步操作。请阅读.NET反应扩展(Rx.NET)相关资料)。


1
为什么不总是返回IObservable<T>而不是IEnumerable<T>?毫无疑问,IObservable<T>是功能的超集吗? - jmcg
不正确。您正在描述Observable<T>(类)。IObservable<T>仅是提供更改通知的接口 - 不一定与集合有关。 - Simon Robinson
在上面回答了你的问题,谢谢Simon指出——我有点累了,本来想写class。 - bbqchickenrobot

1

使用 IEnumerable<T> 表示列表,使用 IObservable<T> 表示事件。非常简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接