Enumerable.AsEnumerable<T>(this IEnumerable<T> source)
的实现只返回source
。然而,Observable.AsObservable<T>(this IObservable<T> source)
返回订阅源的AnonymousObservable<T>
,而不是简单地返回源。
我知道这些方法对于在单个查询中更改monad非常有用(从IQueryable=>IEnumerable开始)。那么,为什么实现方式不同呢?
Observable
版本更具防御性,因为您不能将其强制转换为某个已知类型(如果它最初是作为Subject<T>
实现的,那么您永远无法将它强制转换为已知类型)。那么,为什么Enumerable
版本没有做类似的事情呢?如果我的基础类型是List<T>
,但将其公开为IEnumerable<T>
到AsEnumerable
,则可以强制转换回List<T>
。
请注意,这不是关于如何在不能够强制转换到底层的情况下公开IEnumerable<T>
的问题,而是为什么Enumerable
和Observable
之间的实现在语义上不同的问题。
您的问题由文档回答,我鼓励您在有此类问题时阅读文档。
AsEnumerable的目的是向编译器提示"请停止使用IQueryable,并开始将其视为内存中的集合"。
正如文件所述:
AsEnumerable<TSource>(IEnumerable<TSource>)
方法除了将源的编译时类型从实现IEnumerable<T>
的类型更改为IEnumerable<T>
本身之外没有其他效果。当一个序列实现IEnumerable<T>
但也有一组不同的公共查询方法可用时,AsEnumerable<TSource>(IEnumerable<TSource>)
可用于在查询实现之间进行选择。
如果您想隐藏底层序列的实现,那么如果您不关心您正在生成可变序列,请使用sequence.Select(x=>x)
、ToList
或ToArray
。
AsObservable的目的是隐藏底层集合的实现。正如文件所说:
Observable.AsObservable<TSource>
。。。隐藏可观察序列的身份。
由于这两种方法具有完全不同的目的,因此它们有完全不同的实现。
您对AsEnumerable和AsObservable之间的关系是正确的,包括从基于表达式树的查询切换到内存中查询。
同时,基于受试者<T>是非常常见的,我们需要一种方法来隐藏它(否则用户可以强制转换为IObservable<T>并注入元素)。
很久以前,在Rx预发布的历史中,我们确实有一个单独的Hide方法,它只是一个Select(x=>x)别名。我们从来都不太喜欢它,并决定在一个地方偏离LINQ to Objects的精确镜像,让AsObservable扮演隐藏的角色,这也是基于用户认为它一开始就是这样做的。
不过,请注意,我们在IQobservable<T>也这一点也与AsEnumerable的作用很简单:它充当编译器的提示,让编译器忘记基于表达式树的查询模式,切换到内存中的查询。