为什么IQueryable实现了IEnumerable?

4
我理解为什么会有 IQueryableIEnumerable,但是我不清楚 IQueryable 派生于 IEnumerable 会带来哪些好处。
其中一个我遇到的缺点是:
  • Anyone creating their own IEnumerable extension methods can easily accidentally transform an innocent looking IQueryable query into an inefficient IEnumerable call. For example, using MoreLinq:

     Users.DistinctBy(u => u.Address).Where(u => u.Age >= 18)
    

这将使用 IEnumerable.Where 而不是 IQueryable.Where,除非我错过了上述方法的 IQueryable 实现。

IQueryable 的工作方式,具有一个内部的 Expression,该表达式会被连锁语句所修改,似乎与通常期望的链式函数的工作方式相当不同。

我怀疑这种接口继承具有一些强大的优点,而我可能没有充分利用。与例如执行查询的显式转换相比,拥有 IQueryable 实现 IEnumerable 的好处是什么?


2
根据 Stack Overflow 上由此 API 设计引起的问题数量,我认为你是正确的。 - usr
2
如果你想知道微软为什么做出了他们的决定,那么你需要问他们而不是让我们猜测他们为什么会做出这样的决定,因为这不仅没有任何生产力,也不是一个客观可回答的问题。 - Servy
3
这里有一些微软(MS)程序员/前微软程序员在 Stack Overflow 上,通常这些问题的答案会由他们提供。 - xanatos
2
@xanatos 尽管如此,在曾经成为微软程序员的成千上万人中,只有少数人参与了这个决定。在这里提出只有全世界极少数人能够回答的问题,而且他们阅读这些问题的可能性微乎其微,对于这里的每个人来说都是没有意义的。 - Servy
1
@MichaelParker 在你的编辑之后,这个问题比以前更加基于观点和主观。 - Servy
显示剩余7条评论
1个回答

5

我认为让 IQueryable 继承自 IEnumerable 是愚蠢的... 但是!

它只是有效的

这是 .NET 中集合的主题。如果你想在 List<> 集合上执行 Contains(进行 O(n) 操作),那么就没有安全网了。如果你不知道 IQueryableIEnumerable 之间的区别,多次重新执行相同的查询(即使是 IEnumerable 也不能保证缓存结果 :-) ),那么就没有安全网。

由于这种继承,所有接受 IEnumerable/IEnumerable<T> 的方法都接受 IQueryable/IQueryable<T>/IOrderedQueryable<T>

它只是有效的

无数 VB 程序员对 Variant 数据类型感到满意... 无数 .NET 程序员将对慢查询感到满意 :-)

一如既往,卓越和松散编程都有空间。任何人都有机会 :-)
我要补充的是,你提供的示例来自外部库,其描述为“LINQ to Objects缺少一些理想的功能。”(请注意,“DistinctBy”可以通过“GroupBy”+“Select(First())”来“模拟”,因此它可以构建IQueryable兼容)。所以你像用榔头当螺丝钉,然后抱怨它不是正确的工具 :-)
由Microsoft提供的不产生集合的扩展方法通常是IQueryable安全的(除了Expression vs Func)。
只是出于好奇, MSDN的官方原因是:
“IQueryable接口继承IEnumerable接口,以便如果它表示查询,则可以枚举该查询的结果。枚举导致与IQueryable对象相关联的表达式树被执行。”

该接口继承了IEnumerable<T>接口,因此如果它表示一个查询,那么该查询的结果可以被枚举。枚举强制执行与IQueryable<T>对象关联的表达式树。不返回可枚举结果的查询在调用Execute<TResult>(Expression)方法时被执行。

遗憾的是(至少对我来说),这不是足够好的理由,因为他们本可以

  • 简单地让AsEnumerable()以显式方式完成工作

或者

  • IQueryable(及相关)接口中放置一个GetEnumerator()方法,因为foreach使用鸭子类型,所以它会很高兴。

例如:

public interface IMyEnumerabe<T>
{
    IEnumerator<T> GetEnumerator();
}

IMyEnumerabe<int> myenumerable = null;

foreach (int el in myenumerable)
{
    // Compiles (and in this cases crashes with a NullReferenceException :-) )
}

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