2016-11-11 40 views
4

LINQ有两种计算枚举数的方法:CountLongCount。实际上,这两者之间的唯一区别是第一个返回int,而第二个返回long为什么添加LINQ的LongCount扩展方法有实际的理由吗?

我不清楚为什么添加第二种方法。看起来它唯一的用例是处理超过2B个元素的枚举。这似乎是一个错误的决定对我来说,有几个原因:

  1. 大多数BCL藏品是由一维数组,其具有保证,以适应在int长度的支持。试图通过这将提高OverflowException/OutOfMemoryException

  2. LongCount是O(n),因为IEnumerable是懒惰的。如果你有一个可枚举的3B元素,你可以调用LongCount,然后再次遍历它(如果你想使用任何值,你将不得不这样做),你将会增加额外的3B迭代,这将是非常缓慢,并且从开发者那里隐藏起来。

  3. 由于(1),其他LINQ操作(如ToArray/ToList)不支持具有2B +元素的枚举类型。

我在这里错过了一些东西,还是有更实际的原因为什么LongCount被添加?谢谢。

+0

基于源代码'LongCount'只是通过'Enumerator.MoveNext'遍历IEnumerable',而'Count'试图将'IEnumerable'强制转换为'ICollection'并且使用它的'Count'如果cast不成功,它将迭代'与'LongCount'相同的方式'IEnumerable'。 [https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,d76b4b5d3fd67767](https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs, d76b4b5d3fd67767)。基于@EricLipert的这个“猜测”似乎很合逻辑 – Fabio

+0

@Fabio,这只是一个优化。对于'LongCount'来说''ICollection'也是在语义上是正确的,并且只将结果转换为'long',但'LongCount'没有被使用太多,所以这就是为什么他们没有这么做。 –

回答

4

我对这个设计决定没有第一手的知识,但我可以提供一个有教养的猜测。

该方法对于IQueryable显然有用;查询可以很容易地由一个巨大的数据库表支持。

我希望

IQueryable<Foo> q = whatever; 
long result1 = q.LongCount(); 
long result2 = q.AsEnumerable().LongCount(); 

产生相同的答案。要求内存查询使用不同的方法来返回不同的类型似乎是不合理的,特别是当它很容易实现可枚举的版本时。

但正如我所说,这是一个有教养的猜测;希望有人究竟是谁在这个设计上的合作可能会插入内容。

+1

昨天我打算回答这样的问题,直到我到达'Enumerable.cs'中的'LongCount'实现。它只是列举从开始到结束的顺序。我以为我会找到像'IEnumerable '执行'ICollection '和玩'Count'或谁知道什么的东西。 –

+0

即使我们会讨论某种由网络流支持的枚举类型,在一天结束时,“LongCount”将会逐一检查* long count *。 –

+0

@MatíasFidemraizer:这是不正确的。要扩展Eric Lippert关于巨大数据库表的示例:频繁查询数据库的计数可避免逐一进行,而是依赖索引和元数据来计算次线性时间的计数。一个少数面向数据库的例子就是一个包含重复项的排序随机访问列表。人们可以通过运行稍微修改的二进制搜索来获取O(logn)时间的计数。数据库可以根据索引执行类似的优化。 – Brian

2

我敢肯定它是引入了数据库查询(例如,它应该产生的,而不是COUNT为SQL Server查询COUNT_BIG),但它威力有一些在另一种情况下使用。例如,假设我有这样的方法:

private static Random _r = new Random(1); 
public static IEnumerable<BigInteger> RandomSequence(int upTo) 
{ 
    while (true) { 
     var next = _r.Next(); 
     if (next > upTo) 
      yield break; 
     yield return next; 
    } 
} 

此序列不被任何数组烘焙,也不会在任何地方存储值。因此,它可以轻松生产超过2B件物品。现在假设我想检查一下,需要多少迭代才能生成大于int.MaxValue - 5的数字。如果我这样做:

RandomSequence(int.MaxValue - 5).Count(); 

它会失败,并溢出异常(因为Count方便在内部checked区域包裹增量)。但LongCount来救援!

RandomSequence(int.MaxValue - 5).LongCount(); 

现在我终于想通了,种子1,Random.Next会产生导致大于int.MaxValue - 5在2583066202次迭代!

是的,例子虽然有所收敛,但仍然存在。